paulgorman.org

Bash Shell Scripting

Shell scripts

Shell scripts should be executable (chmod u+x myscript.sh), and the first line of the script should be a shebang interpreter directive pointing to the absolute path of bash.

#!/bin/bash
echo "Hello, world!"

Comments

# Hash marks precede comments.
echo "Hello, world!"

Variables, Quotation Marks, Interpolation & Substitution

~$ foo=bar
~$ echo "The value of 'foo' is $foo."
The value of 'foo' is bar.
~$ myarray=(red blue green); for c in ${myarray[@]}; do echo $c; done
red
blue
green

N.b. = functions as either assignment or equality test, depending on the context.

Double-quoted things ("foo $bar") are treated as a single argument or string, regardless of internal whitespace. Variables inside double quotes are interpolated ($foo would be evaluated).

Single-quoted things ('foo $bar') are also treated as a single argument or string, but variables are note interpolated.

Don't confuse single quotes with backticks (`). Backticks replace a command with the output of that command:

~$ echo `date`
Fri Jan 11 15:10:00 EST 2013

$() does the same thing as backticks, is less ambiguous, and can be nested. echo $(date) is functionally equivalent to the above.

Bash also does arithmetic expansion for expressions between $(( and )):

~$ echo $((2*2))
4

Some of the Built-in Variables

$0
Name of the shell script.
$-
Flags passed to the script.
$1-$N
Arguments passed from the command line.
$#
The number/quantity of command line arguments ($1-$N).
$*
Command line arguments $1-$N.
"$@"
Command line arguments $1-$N, individually quoted.
$?
Return value of last command. Success returns zero, error returns non-zero.
$$
PID of this script.

Conditionals & Loops

if [[ -e "fileexists.txt" ]]; then
    echo "The file exists.";
elif [[ $a = $b ]]; then
    echo "They're equal.";
elif [[ -s "nonemptyfile.txt" ]]; then
    echo "A non-empty file exists.";
else
    echo "The file does not exist.";
fi
for f in ~/tmp/*;
do
    if [[ -d $f ]]; then
        echo "$f is a directory.";
    fi;
done
n=0;
while [[ $n -ne 42 ]];
do
    echo $n;
    n=$(( $n + 1 ));
done
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    *)
        echo $"Usage: $0 {start|stop}"
        exit 1
esac

The [[...]] construct evaluates expressions like the test command (man test).

Further Reading