Home

Syllabus

Notes

Homework

Grades


Shell Programming


Shell Programming
        The basics
        Variables
        Quoting
        Pre-defined variables
        'If' statements
        Conditionals
        Case statement
        Loops


Reading: The Unix Programming Environment, Chapter 5

The basics

  • A shell script is a collection of Unix commands, with a little bit of programming syntax thrown in to make it all work
  • A comment begins with '#' and can start anywhere on the line
  • The first line of the script tells what shell to use to interpret the script, in the form '#!/bin/sh'
  • A really simple example [/bin/true from OpenBSD]:
 
        #! /bin/sh
        #       $OpenBSD: true.sh,v 1.2 1996/06/26 05:41:53 deraadt Exp $
 
        exit 0

Variables

  • Variables are assigned with the syntax var=something
    • Note the lack of spaces
    • If you do have a space, e.g. var = something, the shell will think you're trying to run the command var, with arguments = and something.
  • Variables are referenced by using a "$" in front when referencing the value.
  • If there is any confusion about what the variable name is, you can enclose it in braces like ${var}.
  • Example:
 
        var="This is a variable"
        var1="This is var1"
        echo $var
        echo $var1
        echo ${var}1
  • Produces
 
        This is a variable
        This is var1
        This is a variable1

Quoting

  • There are three main types of quotes
  • Single quote (' -- next to the semicolon on most keyboards)
    • Anything in single quotes is taken literally, not interpreted, and gathered into one argument
  • Double quote (" -- next to the semicolon, use the shift key)
    • Anything in double quotes is interpreted, and gathered into one argument
  • Backquotes (` -- next to 1 on most keyboards)
    • Anything in backquotes is taken as a shell command, and the output of the command is placed on the command line in the place of the backquoted text
  • Example
        today=`date`
        echo 'Running on `uname`: The value of today is $today.'
        echo "Running on `uname`: The value of today is $today."
  • Example output
        Running on `uname`: The value of today is $today.
        Running on Darwin: The value of today is Mon Apr  7 18:08:38 PDT 2003.

Pre-defined variables

  • There are some special variables that are pre-defined for you whenever a shell script is running.
  • $0
    • $0 is the name of the currently running script, as specified to the system
      • If you run the script with "./script", then $0 will be "./script"
      • If you run the script with "/home/joeblow/bin/script", then $0 will be "/home/joeblow/bin/script"
  • $#
    • The number of arguments that were given to the script
  • $*
    • All of the arguments together, but be careful, because you lose quoting (see below)
  • $1, $2, $3, ... $9
    • $n is the nth argument, and is undefined if there was no nth argument
    • The shift command moves every argument "down a notch"
      • $2 becomes $1
      • $3 becomes $2
      • $4 becomes $3
      • etc.
      • (The value of the previous $1 is discarded)
  • $@
    • Similar to $*, but preserves spaces in arguments in certain situations
  • $$
    • The PID of the script
    • Often used to assign unique temporary filenames
  • $?
    • The exit value of the last command run from the script
  • Example
        echo "\$$ is '$$'"
        echo "\$0 is '$0'"
        echo "\$# is '$#'"
        echo "\$* is '$*'"
        echo "\$1 is '$1'"
        echo "\$2 is '$2'"
        echo "\$3 is '$3'"
        shift
        echo "after shift...."
        echo "  \$* is '$*'"
        echo "  \$# is '$#'"
        echo "  \$1 is '$1'"
        echo "  \$2 is '$2'"
        echo "  \$3 is '$3'"
  • Example output
With the command: ./vartest one "two three" four 'five six'
        $$ is '11300'
        $0 is './vartest'
        $# is '4'
        $* is 'one two three four five six'
        $1 is 'one'
        $2 is 'two three'
        $3 is 'four'
        after shift....
          $* is 'two three four five six'
          $# is '3'
          $1 is 'two three'
          $2 is 'four'
          $3 is 'five six'
  • Illustrating the difference between $* and $@
    • The script:
        echo "\$* is $*"
        echo "  Breaking it down...."
        for var in $*
        do
            echo "    $var"
        done
        echo
        echo "\$@ is $@"
        echo "  Breaking it down...."
        echo "  (without the quotes)"
        for var in $@
        do
            echo "    $var"
        done
        echo "  (with the quotes)"
        for var in "$@"
        do
            echo "    $var"
        done
    • Produces:
        When run as ./argtest one "two three" 'four five'
        $* is one two three four five
          Breaking it down....
            one
            two
            three
            four
            five
 
        $@ is one two three four five
          Breaking it down....
          (without the quotes)
            one
            two
            three
            four
            five
          (with the quotes)
            one
            two three
            four five

'If' statements

  • if
    • Syntax
 
        if <<condition>>
        then
            <<one or more statements>>
        fi
    • Example
 
        if [ $var -eq 0 ]
        then
            echo "Don't try to divide by \$var!"
        fi
    • More on what goes in "<<condition>>" in a minute
  • if-then-else
    • Syntax
 
        if <<condition>>
        then
            <<one or more statements>>
        else
            <<one or more statements>>
        fi
    • Example
 
        if [ $var -eq 0 ]
        then
            echo "Don't try to divide by \$var!"
        else
            echo "It's ok to divide by \$var."
        fi
  • if-then-elif-fi
    • Syntax
 
        if <<condition>>
        then
            <<one or more statements>>
        elif <<condition>>
            <<one or more statements>>
        else
            <<one or more statements>>
        fi
    • Example
 
        if [ $var -eq 0 ]
        then
            echo "Don't try to divide by \$var!"
        elif [ $var -eq 1 ]
            echo "There's no point in dividing by \$var."
        else
            echo "It's ok to divide by \$var."
        fi

Conditionals

  • Any Unix command can be used as a conditional
  • If the command returns 0, it's "true"
  • If the command returns anything else, it's "false"
  • One often-used command is the "test" command (which is also aliased as "[" to make your program look more like other programming languages)
    • All sorts of tests on file existence, permissions, etc are possible
    • String and numeric comparisons
    • The test command has a huge number of options, it's best to study the man page to learn them all. Just about anything you want to test can be tested by the test command

Case statement

  • Functionally equivalent to if-then-elif-elif-elif-else-fi
  • But cleaner-looking
  • Matching of cases is done as strings using shell pattern matching (what you'd use with the ls command, for example).
  • Syntax
 
    case $var in
      <<pattern1>>)
        <<things to do for pattern 1>>
        ;;
      <<pattern2>>)
        <<things to do for pattern 1>>
        ;;
      *)  # The default condition
        <<things to do in the default case>>
        ;;
     esac
  • Example (/etc/rc.d/init.d/misc on Geoff Allen’s old laptop)
 
    #!/bin/sh
 
    . /etc/rc.d/init.d/functions
 
    # See how we were called.
    case "$1" in
      start)
          echo -n "Starting misc daemons: "
 
          daemon /usr/local/sbin/wwwoffled
 
          echo
          ;;
      stop)
          echo -n "Stopping misc daemons: "
 
          killproc wwwoffled
 
          echo
          ;;
      status)
          # this is way overkill, but at least we have some status output...
          echo "Status of misc daemons"
 
          status wwwoffled
          ;;
      restart|reload)
          # do not do anything; this is unreasonable
          $0 stop; $0 start
          ;;
      *)
          # do not advertise unreasonable commands that there is no reason
          # to use with this device
          echo "Usage: misc {start|stop|status|restart|reload}"
          exit 1
    esac
 
    exit 0

Loops

  • for
    • Syntax
 
        for var in <<item1>> <<item2>> <<item3>>
        do
            <<statements -- some using $var>>
        done
    • Example ( /etc/rc.d/init.d/killall)
 
    #!/bin/sh
 
    # Bring down all unneeded services that are still running (there shouldn't
    # be any, so this is just a sanity check)
 
    for i in /var/lock/subsys/*; do
        # Check if the script is there.
        [ ! -f $i ] && continue
 
        # Get the subsystem name.
        subsys=${i#/var/lock/subsys/}
 
        # Bring the subsystem down.
        if [ -f /etc/rc.d/init.d/$subsys.init ]; then
            /etc/rc.d/init.d/$subsys.init stop
        else
            /etc/rc.d/init.d/$subsys stop
        fi
    done
  • while
    • Syntax
 
        while <<condition>>
        do
            <<statements>>
        done
  • until
 
        until <<condition>>
        do
            <<statements>>
        done