--author Giovanni Agosta 
--title Bash Scripting Tutorial
--date today
An introduction to Bash Scripting based on the Advanced Bash-Scripting Guide.
--newpage agenda
--heading Agenda

  * Why Shell Programming

  * Basics

  * Variable and Parameter Expansion

  * Control Flow

  * Commands

  * Functions

  * Conclusions
--newpage intro
--heading Why Shell Programming?

  * Essential for System Administration
    - Scripts are used for configuration, boot, etc.

  * Useful for the developer
    - Makefile command are often small scripts
    - You may need to automate small tasks

  * Shell Scripts are quick and easy
    - To develop
    - To maintain
--newpage intro2
--heading When NOT to use Shell Scripts?

  * Computation-intensive tasks

  * Math-intensive programs

  * Cross-platform portability is required

  * Need complex data structure/componentization
--newpage intro3
--heading Bash Scripts

  * Bash stands for "Bourne-Again shell"

  * Derived from a long line of other shells (sh, Bourne shell, Korn Shell,...)

  * The most common shell -- and the one you get by default on most Linux systems
--newpage basics
--heading Basics

Let's start with the classic "Hello, World!" program:

--beginoutput
#!/bin/bash
echo "Hello, World!"
--endoutput

And see what happens...

  => Note that the "#!" pair (sha-bang) specifies which program is used to run the example
  => Otherwise, lines beginning with "#" are comments
--newpage basics2
--heading Basics, part 2
  
  * In general, a shell script can include the invocation of other programs:
--beginoutput
cat hello.sh
--endoutput

  * To make the shell script executable, you can change its permission codes:
--beginoutput
chmod u+rx hello.sh
--endoutput
  
  * Else, you can invoke the shell explicitly:
--beginoutput
bash hello.sh
--endoutput

--newpage basicschars
--heading Basics: Special Characters
 
 * Comments: lines beginning with #
   - Also, ends of lines beginning with #
--beginoutput
echo 'test' # comment
--endoutput

 * Command separator: ';'
--beginoutput
echo 'test' ; echo 'test'
--endoutput

 * Quoting: " and ' preserve part or all the special characters

 * Escaping: \ escapes from special characters (single character only)
--newpage basicschars2
--heading Basics: Special Characters

 * Variable/Parameter substitution: $varname, $num

 * Command substitution: `command` replaces command with its result
--beginoutput
echo `basename $1 .sh`
--endoutput

 * Wild card: *, ? respectively for multi-character and single character wild cards
--newpage basicsredir
--heading Basics: Redirections
--boldon
> &> >& >> < <>
--boldoff

    scriptname >filename redirects the output of scriptname to file filename. Overwrite filename if it already exists.

    command &>filename redirects both the stdout and the stderr of command to filename.

    command >&2 redirects stdout of command to stderr.

    scriptname >>filename appends the output of scriptname to file filename. If filename does not already exist, it is created.
--newpage
--heading Basics: Process Control
  * Ctl-C 	Break. Terminate a foreground job.
  * Ctl-D	Log out from a shell (similar to exit).
  * Ctl-Z 	Pauses a foreground job.
  * &		Start the preceding process in background.
  * bg		Restarts a paused process in background.
  * fg		Brings a process in foreground.
  * |		Pipe. IPC between two processes.
--beginoutput
cat $filename1 $filename2 | grep $search_word
--endoutput
  * $$		Process ID variable
  * $?		Exit status variable
--newpage variables
--heading Variables
  * Variable Assignment and Substitution
--beginoutput
a=879
echo "The value of \"a\" is $a."
--endoutput

  * Variable Assignment with Command Substitution
--beginoutput
a=$(cat /proc/uptime)
echo "The value of \"a\" is $a."
--endoutput
--newpage parameters
--heading Positional Parameters

$0, $1, $2, etc.
   Positional parameters, passed from command line to script, passed to a function, or set to a variable (see Example 4-5 and Example 14-16)

$#
    Number of command line arguments [3] or positional parameters (see Example 33-2)

$*
    All of the positional parameters, seen as a single word
    "$*" must be quoted.

$@
    Same as $*, but each parameter is a quoted string, that is, the parameters are passed on intact, without interpretation or expansion.
--newpage control
--heading Control Flow
  * If

  * While

  * For

  * Case
--newpage controlif
--heading Control Flow: If
--beginoutput
if  (test -e $1) && (test ! -e $2)
then
        mv $1 $2
else
        echo "Either $1 does not exist or $2 already exists"
fi
if [ $1 < $2 ]
then
        a=1
else
        a=0
fi
echo "A=$a"
--endoutput
--newpage controlwhile
--heading While
--beginoutput
echo "Arithmetics"
i=0
while (( $i<10 )) ; do
        echo "$i"
        i=$(($i+1))
done

echo "Strings"
i=0
while (( $i<10 )) ; do
        echo "$i=$(($i))"
        i=$i+1
done
--endoutput
--newpage arith
--heading Arithmetics 
  * In general, an expression is not expanded/evaluated.

  * To perform the arithmetic evaluation, there are two ways:
    - Arithmetic expansion with backticks (often used in conjunction with expr)
--beginoutput
    z=`expr $z + 3`          # The 'expr' command performs the expansion.
--endoutput
    - Arithmetic expansion with double parentheses (as in the example):
--beginoutput
i=$(($i+1))
--endoutput
--newpage for
--heading Control Flow: For
--beginoutput
#!/bin/bash
mkdir out
for i in test/*.txt ; do \
        echo $i ; \
        v=`basename $i .txt` ; \
        echo $v ; \
        cp $i out/${v}.sh ; \
done
ls out
more out/*
rm -rf out
--endoutput
--newpage for2
--heading Control Flow: For (part 2)

The for construct can also run in a way similar to C, though this is less common in shell scripting:

--beginoutput
#!/bin/bash
for (( i=0 ; $i<10 ; i=$i+1 )) do \
        echo $i ; \
done
--endoutput
--newpage case
--heading Control Flow: Case
--beginoutput
# invoked as ". ./test.sh", the shell is interactive
# invoked as "bash ./test.sh", the shell is not interactive
case "$-" in
*i*)    echo This shell is interactive: $- ;;
*)      echo This shell is not interactive: $- ;;
esac
--endoutput
  => Also, checking whether a shell is interactive: "i" flag
  => $- gives the flags of the current shell
--newpage select
--heading Control Flow: Select
--beginoutput
PS3="special prompt!>>"
exit=0;
chmod a+x *.sh
select opt in *.sh "exit" ; do
case "$opt" in
"case.sh") ./case.sh ;;
*".sh"   ) echo "Selected: $opt ($REPLY)" ; ./$opt ;;
"exit"   ) exit=1 ; break ;;
*        ) echo "error!" ;;
esac
if ((exit==1))
        then break ;
fi
done
--endoutput
--newpage functions2
--heading Control Flow: Functions
--beginoutput
function function_name {
command...
}
--endoutput

or

--beginoutput
function_name () {
command...
} 
--endoutput
--newpage functions2
--heading Control Flow: Functions
--beginoutput
#!/bin/bash
PS3='Custom prompt'

menu_as_function()
{
select var
# 'select' uses $@ as list when "in" is not specified
 do
  echo ${var}
  break
 done
}

menu_as_function item1 item2 item3 an_item_out_of_order
# parameters are passed as if the function was a program or shell command
--endoutput
--newpage exercises
--heading Exercises
  * Home Directory Listing
    Perform a recursive directory listing on the user's home directory and save the information to a file. Compress the file, have the script prompt the user to insert a floppy, then press ENTER. Finally, save the file to the floppy.

  * Self-reproducing Script
    Write a script that backs itself up, that is, copies itself to a file named backup.sh.

  * Backwards Listing
    Write a script that echoes itself to stdout, but backwards.
 
  * Automatically Decompressing Files
    Given a list of filenames as input, this script queries each target file (parsing the output of the file command) for the type of compression used on it. Then the script automatically invokes the appropriate decompression command.

