Lecture 2: 14 Jan, 2009


Khaled Harras (With a nod to Mark Stehlik for this lecture

Administrative

Reminder - Lab 1 is out. Start working on it early..


Shell Scripting, Part II

When last we left our intrepid band, we were looking at conditionals and discussed if and elif. We have only a few more constructs to go.

The case statement

As in C or Java, there's also a case, or switch statement, whose form is as follows:

    case var
    in
    pat) command
                command
                ...
                command
                ;; # Two ;;'s serve as the break
    pat) command
                command
                ...
                command
                ;; # Two ;;'s serve as the break
    pat) command
                command
                ...
                command
                ;; # Two ;;'s serve as the break
    esac
  

Here's a quick example that also illustrates the use of printf for formatted output:

    #!/bin/sh

    echo $1
   
    case "$1"
    in
      "+") ans=`expr $2 + $3`
           printf "%d %s %d = %d\n" $2 $1 $3 $ans
           ;;
      "-") ans=`expr $2 - $3`
           printf "%d %s %d = %d\n" $2 $1 $3 $ans
           ;;
     "\*") ans=`expr $2 \* $3`  # do not use * here
           printf "%d %s %d = %d\n" $2 $1 $3 $ans
           ;;
      "/") ans=`expr $2 / $3`
           printf "%d %s %d = %d\n" $2 $1 $3 $ans
           ;;
 
      # the default pattern is a simple *
        *) printf "Don't know how to do that.\n"
           ;;
     esac

  

The for Loop

The for loop provides a tool for processing a list of input. The input to the for loop is a list of values. Each trip through the loop it extracts one value into a variable and then enters the body of the loop. The loop stops when there are no more values left in the list to be extracted.

Let's consider the following example which prints each of the command line arguments, one at a time. We'll extract them from "$@" into $var:

    for var in "$@"
    do
      echo $var
    done
  

Much like C or Java, the shell also has a break command. As you might guess, it can be used to break out of a loop. Consider this example which stops printing command line arguments, when it gets to one whose value is "quit":

    for var in "$@"
    do
      if [ "$var" = "quit" ]
      then
        break
      fi
      echo $var
    done
  

It should be noted that a for loop without a list, e.g., for var, cycles through all the arguments typed on the command line as if you had used for var in "$@".

One last note — there's also an exit command, which takes the form exit n, where n is the exit status code that you want to return. If none is provided, the exit status of the last command to be executed prior to the exit will be returned.

The while and until Loops

The shell has a while loop similar to that seen in C or Java. It continues until the predicate is false. And, like the other loops within shell, break (and continue) can be used. Here's an example of a simple while loop that processes a variable number of command-line arguments, throwing the first one away after it's been handled:

    while [ "$#" -ne 0 ]
    do
      echo "$1"
      shift
    done
  

There is a similar loop, the until loop that continues until the condition is true — in other words, while the command fails. Its form is:

    until command
    do
      command
      command
    done
  

Input/Output Redirection and Pipes

We saw the other day that we can redirect the output of a command to a file, instead of standard output (the console), via >:

    cat > test-file
    first line
    second line
    ^D  # end-of-file
  

Similarly, the shell allows us to redirect input into a command from a file instead of standard input (the keyboard) by using <.

The shell also allows us to use the output of one program as input to another via a mechanism called a pipe, |, seen here

    users | wc
  

grep

grep is the standard tool for searching a text file for a substring that matches a particular pattern. Often times this pattern is a literal, such as a word. But, on other occasions, it can be something defined using a regular expression (more on those when we get to perl). Grep assumes the input files are text files, rather than binary files. It assumes that the file is organized into one or more "lines" and reports lines that contain matches for the provided pattern. RTFM for the details of grep, but the basics are as follows:

    grep [flags] [pattern] [file_list]
  

The pattern can be any regular expression. The file_list is a space-separated list of the files to search. This list can include things defined using wildcards. The flags control the details of the way grep works. Commonly used flags include the following:

cut

cut is a quick and dirty utility that comes in handy across all sorts of scripting. It selects one portion of a string. The portion can be determined by some range of bytes, some range of characters, or using some delimiter-field_list pair.

The example below prints the first three characters (-c) of each line within the file:

    cat file.txt | cut -c1-3
  

The next example uses a comma as a field delimiter and prints the third and fifth fields within each line. In this respect lines are treated as records:

    cat file.txt | cut -d, -f3,5
  

In general, the ranges can be expressed as a single number, a comma-separated list of numbers, or a range using a hyphen (-).

tr

The tr command translates certain characters within a file into certain other characters. It works with bytes within a binary file as well as characters within a text file.

tr accepts as arguments two quoted strings of equal length. It translates characters within the first quoted string into the corresponding character in the second quoted string. The example below converts a few lowercase letters into uppercase:

    cat file.txt | tr "abcd" "ABCD" > outfile.txt
  

tr can also accept ranges, as seen below:

    cat file.txt | tr "a-z" "A-Z" > outfile.txt
  

The "-d" option can be used to delete, outright, each and every instance of a particular character. The example below, for example, removes newlines ('\n') from a file:

    cat file.txt | tr -d "\n" > outfile.txt
  


Aside: special characters can be represented by escaping their octal value. For example, '\r' can be represented as '\015' and '\n' as '\012'. man ascii if you'd like to see the character-to-number mapping.