Friday, November 21, 2008

Defensive Scripting, The Double Quote Way

If you want to ensure your shell script is fool-proof and that it will not break in all kind of situation, you should script it in a 'defensive' way. What I mean defensive is do not assume things. Often time people assume that their variable is set with non-empty value and they will try to use the variable value to compare with some pre-defined value. If the variable value is an empty string, your test condition will fall apart if it is not double quoted. Here I am going to simulate this type of situation where the 'ostype' variable will be empty because the command to run does not exist.
$ cat c.sh
#! /bin/sh


ostype=`unknown_cmd_get_os`

if [ $ostype = unix ]; then
        echo unix
else
        echo windows
fi

echo here

$ ./c.sh
./c.sh: line 4: unknown_cmd_get_os: command not found
./c.sh: line 6: [: =: unary operator expected
windows
here

In the above execution, the 'if' command thrown exception because shell carried out command and variable substitutions before the shell parsed the command line. Here are the steps the shell will go through

  1. if [ $ostype = unix ]; then ... is typed in before you hit the return button
  2. Upon hitting the return button, the shell will do substitution and in our case it will do variable substitution by replacing the $ostype with it's value. So the command going to the shell will be
    if [ = unix ]; then ...
  3. When shell read from the standard input, it knows that you are trying to run the "if" command
  4. The if" command is a built-in shell syntax and the correct syntax should be 'if' 'condition' 'then' .... 'fi'
  5. Syntax for 'condition' should be '[' 'expr' ']' , and 'expr' is a conditional expression
  6. What you want is to compare the value of variable "ostype" with the string "unix". However, after the variable substitution there isn't anything there and your 'expr' becomes '= unix' which is an incorrect conditional expression.
  7. Exception occurred.

To be 'defensive', you can do either the following:

  • Double quote everything. As you can see, it does not break the if statement because you are comparing an empty string with the string "unix"
    $ cat c.sh
    #! /bin/sh
    
    
    ostype=`unknown_cmd_get_os`
    
    if [ "$ostype" = "unix" ]; then
            echo unix
    else
            echo windows
    fi
    
    echo here
    
    $ ./c.sh
    ./c.sh: line 4: unknown_cmd_get_os: command not found
    windows
    here
    
  • Prepend some arbitrary character(s), in our case a character 'x', to ensure the string is non-empty. With this technique you do not necessarily need to double quote the string
    $ cat c.sh
    #! /bin/sh
    
    
    ostype=`unknown_cmd_get_os`
    unix="unix"
    
    if [ x$ostype = x$unix ]; then
            echo unix
    else
            echo windows
    fi
    
    echo here
    
    $ ./c.sh
    ./c.sh: line 4: unknown_cmd_get_os: command not found
    windows
    here
    

IMHO, the best practice will be to double quote all your string. So next time when you script your string comparison, remember to double quote them.

Labels:

1 Comments:

Blogger Raymond Tay said...

Good intro to shell quoting and pitfalls

10:56 AM  

Post a Comment

<< Home