Chapter 32. Scripting With Style

Get into the habit of writing shell scripts in a structured and systematic manner. Even "on-the-fly" and "written on the back of an envelope" scripts will benefit if you take a few minutes to plan and organize your thoughts before sitting down and coding.

Herewith are a few stylistic guidelines. This is not intended as an Official Shell Scripting Stylesheet.

32.1. Unofficial Shell Scripting Stylesheet


Example 7-2. Equivalence of test, /usr/bin/test, [ ], and /usr/bin/[

   1 #!/bin/bash
   2 
   3 echo
   4 
   5 if test -z "$1"
   6 then
   7   echo "No command-line arguments."
   8 else
   9   echo "First command-line argument is $1."
  10 fi
  11 
  12 echo
  13 
  14 if /usr/bin/test -z "$1"      # Equivalent to "test" builtin.
  15 #  ^^^^^^^^^^^^^              # Specifying full pathname.
  16 then
  17   echo "No command-line arguments."
  18 else
  19   echo "First command-line argument is $1."
  20 fi
  21 
  22 echo
  23 
  24 if [ -z "$1" ]                # Functionally identical to above code blocks.
  25 #   if [ -z "$1"                should work, but...
  26 #+  Bash responds to a missing close-bracket with an error message.
  27 then
  28   echo "No command-line arguments."
  29 else
  30   echo "First command-line argument is $1."
  31 fi
  32 
  33 echo
  34 
  35 
  36 if /usr/bin/[ -z "$1" ]       # Again, functionally identical to above.
  37 # if /usr/bin/[ -z "$1"       # Works, but gives an error message.
  38 #                             # Note:
  39 #                               This has been fixed in Bash, version 3.x.
  40 then
  41   echo "No command-line arguments."
  42 else
  43   echo "First command-line argument is $1."
  44 fi
  45 
  46 echo
  47 
  48 exit 0

The [[ ]] construct is the more versatile Bash version of [ ]. This is the extended test command, adopted from ksh88.

Note

No filename expansion or word splitting takes place between [[ and ]], but there is parameter expansion and command substitution.

   1 file=/etc/passwd
   2 
   3 if [[ -e $file ]]
   4 then
   5   echo "Password file exists."
   6 fi

Tip

Using the [[ ... ]] test construct, rather than [ ... ] can prevent many logic errors in scripts. For example, the &&, ||, <, and > operators work within a [[ ]] test, despite giving an error within a [ ] construct.

Note

Following an if, neither the test command nor the test brackets ( [ ] or [[ ]] ) are strictly necessary.
   1 dir=/home/bozo
   2 
   3 if cd "$dir" 2>/dev/null; then   # "2>/dev/null" hides error message.
   4   echo "Now in $dir."
   5 else
   6   echo "Can't change to $dir."
   7 fi
The "if COMMAND" construct returns the exit status of COMMAND.

Similarly, a condition within test brackets may stand alone without an if, when used in combination with a list construct.
   1 var1=20
   2 var2=22
   3 [ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
   4 
   5 home=/home/bozo
   6 [ -d "$home" ] || echo "$home directory does not exist."

The (( )) construct expands and evaluates an arithmetic expression. If the expression evaluates as zero, it returns an exit status of 1, or "false". A non-zero expression returns an exit status of 0, or "true". This is in marked contrast to using the test and [ ] constructs previously discussed.


Example 7-3. Arithmetic Tests using (( ))

   1 #!/bin/bash
   2 # Arithmetic tests.
   3 
   4 # The (( ... )) construct evaluates and tests numerical expressions.
   5 # Exit status opposite from [ ... ] construct!
   6 
   7 (( 0 ))
   8 echo "Exit status of \"(( 0 ))\" is $?."         # 1
   9 
  10 (( 1 ))
  11 echo "Exit status of \"(( 1 ))\" is $?."         # 0
  12 
  13 (( 5 > 4 ))                                      # true
  14 echo "Exit status of \"(( 5 > 4 ))\" is $?."     # 0
  15 
  16 (( 5 > 9 ))                                      # false
  17 echo "Exit status of \"(( 5 > 9 ))\" is $?."     # 1
  18 
  19 (( 5 - 5 ))                                      # 0
  20 echo "Exit status of \"(( 5 - 5 ))\" is $?."     # 1
  21 
  22 (( 5 / 4 ))                                      # Division o.k.
  23 echo "Exit status of \"(( 5 / 4 ))\" is $?."     # 0
  24 
  25 (( 1 / 2 ))                                      # Division result < 1.
  26 echo "Exit status of \"(( 1 / 2 ))\" is $?."     # Rounded off to 0.
  27                                                  # 1
  28 
  29 (( 1 / 0 )) 2>/dev/null                          # Illegal division by 0.
  30 #           ^^^^^^^^^^^
  31 echo "Exit status of \"(( 1 / 0 ))\" is $?."     # 1
  32 
  33 # What effect does the "2>/dev/null" have?
  34 # What would happen if it were removed?
  35 # Try removing it, then rerunning the script.
  36 
  37 exit 0