next up previous contents
Next: Initialisation Files Up: Shells Previous: Aliases

Scripts

Scripts are text files that contain a series of shell commands. When the execute permission is set on such a text file, it can be run like a program and the commands in the file are executed in sequence.

This in it self is no great achievement, yet the extent of the commands provided by most UNIX shells makes these scripts very powerful tools.

Scripts can take arguments as command line parameters. Say we create a script that will display the contents of a file in our home directory and we want to be able to specify the file name without the directory name on the command line. If our script were called homecat we could then run the script to display the file a in our home directory by typing homecat a. The script would look like this:

cat ~/$1

The $1 denotes the first command line argument and it is possible to specify more than one argument on the command line.

We can use the shell's echo command to display output on the screen. bash also provides basic arithmetic commands. If we want to write a shell script that adds two numbers, which are passed on the command line, we can use these two functions together:

echo $[ $1 + $2 ]

Shell can also iterate over a series of items in a list. We could write a little script that printed out the following:

Hello Harry !
Hello Sally !
Hello Robert !
Hello Victoria !
To do this we have to use the shell's for loop which iterates over a list of words:
#!/bin/sh
for i in Harry Sally Robert Victoria; do
  echo Hello $i !
done
i is set to a different value each time the for loop is iterated over. This can also be used when renaming files. Say we want to rename all files in a directory, such that they all have a .new appended to them:

$ls
m      n      o    
$~/rename
$ls
m.new  n.new  o.new
$

We cannot use the move command:

$mv * *.new
mv: when moving multiple files, last argument must be a directory
$

We have to write a script that takes one file at a time and moves it to the new file name. We can tell the for loop to take the list of all files in the current directory, which is '' and assign the variable i a different file name on each iteration:

#!/bin/sh
for i in * ; do
  { mv $i $i.new ; }
done

Another useful shell command is the while loop. It allows us to run through a loop while some condition is true. If we wanted to print the numbers 1 to 5 we could use the following script:

#!/bin/bash
i=0

while [ $i -le 5 ]; do
  echo $i
  i=$[$i+1]
done
The [ is actually a command called test. To find out more about it type man test.

We can use the while loop in a similar way to the for loop in renameing files:

#!/bin/sh
ls | while read i; do
  { mv $i $i.new ; }
done

Here we are reading the output of the ls command into the variable i one line on every iteration. Usually the while loop is used when it is not clear at the start of the loop how many iterations there will be.

Up to now we have not been able to influence the flow of control once a script has started and we have had to execute all commands from start to finish. Sometimes it is useful not to execute all commands. The if statement allows us to do this.

We can put some safety into our rename script by first asking the user if the files should be really renamed:

#!/bin/sh
echo "Do you want to rename all files (y/n)"
read i

if [ "$i" = "y" ]; then 
  echo "Renaming files..."
  for i in * ; do
    { mv $i $i.new ; }
  done
else
  echo "Aborting..."
fi

The case statement is i similar command. It allows the shell script to make decisions of what to do. Say we want to extend our rename script to give us the option the rename a file or not:

$~/rename
Do you want to rename the file m (y/n/?)
?
If you answer with 'y' the file m will be renamed.
If you answer with 'n' the file m will not be renamed. 
n
Skipping m
Do you want to rename the file n (y/n/?)
n
Skipping n
Do you want to rename the file o (y/n/?)
y
Renameing data
$

The shell script needed gets more complicated:

#!/bin/sh

for i in * ; do
  j=?
  echo "Do you want to rename the file $i (y/n/?)"
  while [ "$j" = "?" -o "$j"]; do
    read j
    case $j in 
      ("y") echo Renameing $i
          mv $i $i.new 
          ;;
      ("n") echo Skipping  $i
          ;;
      ("?") echo "If you answer with 'y' the file $i will be renamed."
          echo "If you answer with 'n' the file $i will not be renamed. "
          ;;
    esac
  done
done

Shell scripts can be very powerful, but can also be very hard to understand, like the following on which lists all file names and file lengths in a directory:

#!/bin/sh
ls -l > /tmp/ls.$$

while read i; do
        fname=`expr "$i" : '.* \(.*\)$'`
        fsize=`expr "$i" : '[-drwxXsStT]*[ ]+[0-9]+
		[ ]+[0-9a-zA-Z]+[ ]+[0-9a-zA-Z]+[ ]+
		\([0-9]+\)[ ]+.*$'`
        echo -e $fname "\t\t\t" $fsize
done < /tmp/ls.$$

rm /tmp/ls.$$

To avoid some of the confusion it is possible to break up shell scripts into functions, though most users only use very basic scripts, and would not be expected to be able to understand scripts like the previous one.



next up previous contents
Next: Initialisation Files Up: Shells Previous: Aliases



Mark O. Stitson
Wed Sep 25 10:45:32 BST 1996