Scripting With Style

Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev |  | Next

# Chapter 35. 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 (necessarily) intended as an Official Shell Scripting Stylesheet.

# 35.1. Unofficial Shell Scripting Stylesheet

- Comment your code. This makes it easier for others to understand (and appreciate), and easier for you to maintain.

         1 PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
         2 #  It made perfect sense when you wrote it last year,
         3 #+ but now it's a complete mystery.
         4 #  (From Antek Sawicki's "pw.sh" script.)

  Add descriptive headers to your scripts and functions.

         1 #!/bin/bash
         2
         3 #************************************************#
         4 #                   xyz.sh                       #
         5 #           written by Bozo Bozeman              #
         6 #                July 05, 2001                   #
         7 #                                                #
         8 #           Clean up project files.              #
         9 #************************************************#
        10
        11 E_BADDIR=85                       # No such directory.
        12 projectdir=/home/bozo/projects    # Directory to clean up.
        13
        14 # --------------------------------------------------------- #
        15 # cleanup_pfiles ()                                         #
        16 # Removes all files in designated directory.                #
        17 # Parameter: $target_directory                              #
        18 # Returns: 0 on success, $E_BADDIR if something went wrong. #
        19 # --------------------------------------------------------- #
        20 cleanup_pfiles ()
        21 {
        22   if [ ! -d "$1" ]  # Test if target directory exists.
        23   then
        24     echo "$1 is not a directory."
        25     return $E_BADDIR
        26   fi
        27
        28   rm -f "$1"/*
        29   return 0   # Success.
        30 }
        31
        32 cleanup_pfiles $projectdir
        33
        34 exit $?

- Avoid using "magic numbers," [1] that is, "hard-wired" literal constants. Use meaningful variable names instead. This makes the script easier to understand and permits making changes and updates without breaking the application.

         1 if [ -f /var/log/messages ]
         2 then
         3   ...
         4 fi
         5 #  A year later, you decide to change the script to check /var/log/syslog.
         6 #  It is now necessary to manually change the script, instance by instance,
         7 #+ and hope nothing breaks.
         8
         9 # A better way:
        10 LOGFILE=/var/log/messages  # Only line that needs to be changed.
        11 if [ -f "$LOGFILE" ]
        12 then
        13   ...
        14 fi

- Choose descriptive names for variables and functions.

         1 fl=`ls -al $dirname`                 # Cryptic.
         2 file_listing=`ls -al $dirname`       # Better.
         3
         4
         5 MAXVAL=10   # All caps used for a script constant.
         6 while [ "$index" -le "$MAXVAL" ]
         7 ...
         8
         9
        10 E_NOTFOUND=95                        #  Uppercase for an errorcode,
        11                                      #+ and name prefixed with E_.
        12 if [ ! -e "$filename" ]
        13 then
        14   echo "File $filename not found."
        15   exit $E_NOTFOUND
        16 fi
        17
        18
        19 MAIL_DIRECTORY=/var/spool/mail/bozo  #  Uppercase for an environmental
        20 export MAIL_DIRECTORY                #+ variable.
        21
        22
        23 GetAnswer ()                         #  Mixed case works well for a
        24 {                                    #+ function name, especially
        25   prompt=$1                          #+ when it improves legibility.
        26   echo -n $prompt
        27   read answer
        28   return $answer
        29 }
        30
        31 GetAnswer "What is your favorite number? "
        32 favorite_number=$?
        33 echo $favorite_number
        34
        35
        36 _uservariable=23                     # Permissible, but not recommended.
        37 # It's better for user-defined variables not to start with an underscore.
        38 # Leave that for system variables.

- Use exit codes in a systematic and meaningful way.

         1 E_WRONG_ARGS=95
         2 ...
         3 ...
         4 exit $E_WRONG_ARGS

  See also Appendix E.

  Ender suggests using the exit codes in `/usr/include/sysexits.h` in shell scripts, though these are primarily intended for C and C++ programming.

- Use standardized parameter flags for script invocation. Ender proposes the following set of flags.

         1 -a      All: Return all information (including hidden file info).
         2 -b      Brief: Short version, usually for other scripts.
         3 -c      Copy, concatenate, etc.
         4 -d      Daily: Use information from the whole day, and not merely
         5         information for a specific instance/user.
         6 -e      Extended/Elaborate: (often does not include hidden file info).
         7 -h      Help: Verbose usage w/descs, aux info, discussion, help.
         8         See also -V.
         9 -l      Log output of script.
        10 -m      Manual: Launch man-page for base command.
        11 -n      Numbers: Numerical data only.
        12 -r      Recursive: All files in a directory (and/or all sub-dirs).
        13 -s      Setup & File Maintenance: Config files for this script.
        14 -u      Usage: List of invocation flags for the script.
        15 -v      Verbose: Human readable output, more or less formatted.
        16 -V      Version / License / Copy(right|left) / Contribs (email too).

  See also Section G.1.

- Break complex scripts into simpler modules. Use functions where appropriate. See Example 37-4.

- Don't use a complex construct where a simpler one will do.

         1 COMMAND
         2 if [ $? -eq 0 ]
         3 ...
         4 # Redundant and non-intuitive.
         5
         6 if COMMAND
         7 ...
         8 # More concise (if perhaps not quite as legible).

| ... reading the UNIX source code to the Bourne shell (/bin/sh). I was shocked at how much simple algorithms could be made cryptic, and therefore useless, by a poor choice of code style. I asked myself, "Could someone be proud of this code?" --Landon Noll

### Notes

[1] | In this context, "magic numbers" have an entirely different meaning than the magic numbers used to designate file types.

Prev | Home | Next
Gotchas | Up | Miscellany