Notes: Unix Lab 13

Basic shell scripting

  1. What is a shell script

    A shell script is nothing more than a plain text file which contains one or more shell commands. These commands can be written with any text editor, such as vi, but not a word processor unless the file is specifically saved in a plain text format. Shell scripting is one area where the differences betweeen the various unix shells can make a big difference - be sure to use the correct syntax for the shell you are scripting for. We will be using bash syntax for this lesson as it is the default for the lab.

    The commands in the shell are read one line at a time by the shell and executed in order. An extremely simple form of a shell script is shown below:

    (zero.sh)
    #!/bin/bash
    
    cd /home/mark
    pwd 
    date
    
    

    If we were to take the contents of this file (shown above) and save it to a file named "zero.sh" it could be read by the shell, bash, to run the various commands it contains. The file name extension (.sh) does not have any bearing on the function of the script; it is used only to visually identify the file to the user.

     
           
    [mark@iguana unix]
    [mark@iguana unix] bash ./zero.sh
    /home/mark
    Thu Mar  4 20:56:54 EST 2004
    [mark@iguana unix]
    
    Notice that the actual command executed here was the shell bash and that the path and name of the shell script was the argument to that command. The shell bash then read the contents of the file executing them in the order that they were entered in the file. The script commands do not appear on the terminal output, just their results. Commands that do not print inforamtion to standard out are completely silent, such as the cd command in the example above.

    There are generally a few more elements in a shell script and the script itself can be set as an executable file which will be explained further on.

  2. Basic shell script elements


    Below is an example of a more complete but still relatively simple shell script. This shell script could be run independently by setting the execute permissions on the file.

    (one.sh)
    #!/bin/bash
    
    # comments follow the "#" character
    
    echo "Hello World"
    
    exit    # another comment following a command on the same line.
    # done
    
    

    There are several items to take note of here:

    To run this shell script, it would need to be saved to a file. It will be named "one.sh" in this example. It may be run by using the file name as an argument to the shell interpreter as was shown previously. That is not always convenient as you'd have to find out what shell it was written for before running it. Since we have defined the appropriate shell at the beginning of the first line with the #!/bin/bash syntax, all that needs to be done is to set the file permissions to allow execution of the file.

     
    [mark@iguana unix]
    [mark@iguana unix] chmod u+x one.sh
    [mark@iguana unix] ./one.sh
    Hello World
    [mark@iguana unix] /home/mark/work/unix/one.sh
    Hello World
    [mark@iguana unix]
    

    It is important to remember that the shell which is running the shell script is not the same instance of the shell that is being used interactively by the user. The script runs in a separate, unique instance of the the command shell. Its environment variables, working directory and other parameters may be manipulated and changed independent of the interactive shell which is used to start the script.

  3. Shell Variables

    Like other programming languages, Unix shells can use variables to store temporary information. These are available from the shell's environment (remember env?). These can be manipulated with the commands export and unset. In addition to the environment variables there are also shell variables which can be used in a similar manner but with a different syntax. Use the = sign to set and unset to delete. The values of either type of variable can be used by using the variable name with a "$" character preceding it. See the examlel below, "two.sh".

    (two.sh)
    #!/bin/bash
    
    # environment variables may be used in shell scripts
    echo $HOME
    
    # shell variables may be assigned values with the = sign
    testpath=/opt/kde
    ls -ld $testpath
    
    # shell variables may be removed with the unset command
    unset testpath
    echo $testpath
    
    exit
    # done
    

    When executed, this script produces the following output:
     
    > ./two.sh
    /home/mark
    drwxr-xr-x    9 root     root         2048 Feb 23 10:44 /opt/kde
    
    >
    

  4. Shell Arithmatic

    Basic arithmatic can be performed in a shell script (or on the command line) with the expr command. expr has several other functions besides performing mathematical calculations - see the man pages if interested. Below are some simple examples of addition, subtraction, multiplication and division:
    (three.sh)
    #!/bin/csh
    
    # addition
    a = `expr 2 + 2`
    echo Addition equals $a
    
    # subtraction
    b = `expr $a - 1`
    echo Subtraction equals $b
    
    # multiplication (note that the * symbol needs to be escaped with the \ )
    c = `expr $a \* $b`
    echo Multiplication equals $c
    
    # division
    d = `expr $c / 2`
    echo Division equals $d
    
    # more complex operations can be done as well.
    # parenthesis are reserved characters and need to be escaped as well
    expr \( -4 \* 3 \) + 2
    
    exit
    # done
    
    

    In order to assing the output value of the expr command to a variable, the command must be placed between backtics "`". This causes the expr command to be run/evaluated before assigned to the variable.

    Below is the output from the script "three.sh":
     
    > expr 5 + 11
    16
    > ./three.csh
    Addition equals 4
    Subtraction equlas 3
    Multiplication equals 12
    Division equals 6
    -10
    >
    

  5. Comparison Functions

    There are many different ways that conditions may be tested in a shell script. Below is a brief summary of those tests available. The next section, "Conditional Expressions", shows a few examples of how these tests may be used.

    File tests:
    -r filename Returns "true" if the filename exists and is readable
    -w filenameReturns "true" if the filename exists and is writable
    -x filenameReturns "true" if the filename exists and is executable
    -f filenameReturns "true" if the filename exists and is a regular file
    -d filenameReturns "true" if the filename exists and is a directory
    -h filenameReturns "true" if the filename exists and is a symbolic link
    -c filenameReturns "true" if the filename exists and is a character special file
    -b filenameReturns "true" if the filename exists and is a block special file
    -p filenameReturns "true" if the filename exists and is a named pipe
    -u filenameReturns "true" if the filename exists and its set-user-id bit is set
    -g filenameReturns "true" if the filename exists and its group-user-id bit is set
    -k filenameReturns "true" if the filename exists and its sticky bit is set
    -s filenameReturns "true" if the filename exists and has a size greater than 0
    file1-nt file2Returns "true" if file1 is newer than file2
    file1-ot file2Returns "true" if file1 is older than file2
    file1-ef file2Returns "true" if file1 and file2 are the same file

    String Tests:
    -z stringReturns "true" if the length of the string is 0
    -n stringReturns "true" if the length of the string is > 0
    string1 = string2Returns "true" if string1 and string2 are identical
    string1 != string2Returns "true" if string1 and string2 are not identical
    stringReturns "true" if the string is not NULL
    string1 < string2Returns "true" if string 1 is alphabetically before string2
    string1 > string2Returns "true" if string 1 is alphabetically after string2

    Integer Tests:
    n1 -eq n2Returns "true" if the integers n1 and n2 are algebraically identical
    n1 -ne n2Returns "true" if the integers n1 and n2 are notalgebraically identical
    n1 -gt n2Returns "true" if the integer n1 is algebraically greater than the integer n2
    n1 -ge n2Returns "true" if the integer n1 is algebraically greater than or equal to the integer n2
    n1 -lt n2Returns "true" if the integer n1 is algebraically less than the integer n2
    n1 -le n2Returns "true" if the integer n1 is algebraically less than or equal to the integer n2

  6. Conditional Expressions

    bash can control the execution of commands in a shell script with the use of if, then, and else. The if statements use the test operators listed in the previous section. Below are several examples:

    Example 1 (file test) (four.sh):
    #!/bin/bash
    
    # check if the temp directory exists before changing directories.
    if [ -d $HOME/temp ] then
        cd $HOME/temp
        ls *.dat
    fi   
    

    Example 2 (integer comparison) (five.sh):
    #!/bin/bash
    
    # check if the variable $var1 is greater than 600
    var1=200
    if [ $var1 -gt 600  ] ;then
        echo $var1 is larger
    else
        echo $var2 is smaller
    fi   
    #
    

    Example 3 (string comparison) (six.sh):
    #!/bin/bash
    
    myname="george"
    # check if the variable $myname equals the shell environment variable $LOGNAME
    if [ $myname = $LOGNAME  ] ;  then
        echo $myname is $LOGNAME
    else
        echo $myname is not $LOGNAME
    fi   
    #
    

    Multiple items can be tested with the case statement. Each case statement is tested against the the value of the variable $VAL specified in the case block. If none of the tests return "true" then the default item is executed.

    Example 4 (case statement) (seven.sh):
    #!/bin/bash
    
    VAL="No"
    echo val is $VAL
    
    case $VAL in
       yes)
        echo $VAL equals yes
        ;;
       No)
        echo $VAL is No
        date
        ;;
       X)
        echo value is $VAL
        date +%Y%m%d
        ;;
      *)
        echo Default option $VAL
        date +%j%Y
        ;;
    esac
    #
    

  7. Looping Expressions

    Below is an example of a for loop nested within an if block.

    (eight.sh)
    #!/bin/bash
    
    VAL="no"
    
    if [ $VAL != "yes" ];  then
        for LTR ( A B C D 0 9 8 7 6 5 George )
            do
              echo X $LTR ...
        done
    else
        echo failing because val is $VAL 
    fi
    #
    

  8. Other Resources:
    Introduction to Shell Scripts

    Csh Programming Considered Harmful