Linux Day 3 - Bash Shell Scripting
Linux Day 3 - Bash Shell Scripting
May 2012
1 Introduction
In the previous sessions, you have been using basic commands in the shell.
The bash shell environment is in fact a complete programming environment.
The large set of commands can be combined with programming constructs to
perform powerful and useful functions. In the next sessions you will see how
this can be done.
2 Script Files
A script file is a plain text file that contains commands that can be interpreted
by the bash shell. Since it is a plain text file, you may edit or create it using
your favourite text editor (Vim?). Conventionally bash script files are named
with the extension .sh. In order to let the OS know that a file is in fact a bash
script file, you must
1. Set the file to be executable, for instance
This tells the OS that this file contains a program and is executable. The
next step tells the OS how to actually execute it.
2. Set the first line of your script to be
#!/bin/bash
This will tell the OS to use bash to execute the commands in your file
3 Beginning to Script
3.1 Sequence of commands
The simplest form of scripting is to simply put a sequence of commands in a file.
These are the same commands that you would type at the shell prompt. You
1
can create a simple script file script1.sh using any editor with the following
lines in it and save it.
#!/bin/bash
4 Shell Plumbing
In the bash shell, the pipe or | symbol connects two commands together. For
example the line
command1 | command2
connects the standard output (stdout) of command1 to the standard input (stdin)
of command2. Consider a simple example. The sort command sorts the lines
of a text file alphabetically and writes it to stdout. The uniq command omits
repeated lines in files and writes out the result to stdout (read their man pages
to know more).
Consider the text file fruits.txt containing the following lines
2
apple
mango
orange
pineapple
mango
strawberry
strawberry
apple
grapes
We wish to remove duplicate lines in this file. Since uniq will only omit
consecutive duplicate lines, we need to sort the file alphabetically first. We
would like to take advantage of shell pipes to do this in one step. Our script to
perform this uniquefruits.sh will look like.
#!/bin/bash
5 Redirection
Suppose you would like the output of a command to be written to a file rather
than the screen. The shell provides the redirection operator to achieve that.
Redirection has several variants, but right now we will use the output redirection
operator ‘>’. If we use
3
as passing a command line argument to the program. We would like to invoke
uniquefruits.sh with the name of the input file as a command line argument
as such
./uniquefruits.sh fruits.txt
There are special variables that are accessible within a script. These vari-
ables contain the command line arguments passed to the command. The first
command line argument is in variable $1, the second in $2 and so on. The
UNIX convention is that the zeroth command line argument is the command
name itself. Thus if our script is executed as
./uniquefruits.sh fruits.txt vegetables.txt
the variables will have the following values.
$0 ./uniquefruits.sh
$1 fruits.txt
$2 vegetables.txt
Rewriting our script so that it accepts the name of the input file as a com-
mand line argument, we have, uniquefruits2.sh
#!/bin/bash
grep searches for PATTERN within the input FILE or stdin and prints any
line that matches PATTERN. For now consider that PATTERN is just a word
or string. For example if we run grep on the file fruits.txt that we created
earlier
grep apple fruits.txt
The output is
apple
pineapple
apple
4
Notice that grep finds a match even when PATTERN is within another string,
in this case ‘apple’ is found within ‘pineapple’. In order to match only whole
words, we pass the -w option to grep.
grep, like other commands, also returns an exit status. It returns 0 if it finds
a match and non-zero if it doesn’t or encounters an error. We will use the exit
status of grep to write a very naı̈ve script isscript.sh that tells us whether
a file is a bash shell script or not. We will have to use a conditional expression
to do this.
#!/bin/bash
This redirects the stdout of the grep command to the file /dev/null. /dev/null
is a UNIX device special file. That is, it does not really exist on disk. Any data
written to this file is simply lost and is not stored anywhere.
Besides the exit status of commands, if can be used in conjuction with the
[ ] construct to test a variety of conditions on files and variables (type help
test for details). For example to test if the pathname contained in $1 exists,
use
if [ -e "$1" ]
The condition is true if the file exists. To negate the condition use
if [ ! -e "$1" ]
String comparison can also be done using,
if [ "$1" = "" ]
5
which is true if no command line argument was passed. Notice the spaces be-
tween the keywords (‘=’, ‘-e’), brackets and variables, these spaces are required.
Other familiar operators are <, >, !=, all of which operate lexicographically. The
equivalent arithmetic operators are -eq, -lt, -gt and -ne (see help test for
a full listing).
Conditions can be combined by using boolean operators
if [ $a -eq 40 ] && [ $b -eq 51 ]
Which is true if both variable a equals 40 and variable b equals 51. If we wanted
a logical or instead of and, we would use.
if [ $a -eq 40 ] || [ $b -eq 51 ]
8 Loops
Loops are used to execute a sequence of commands repeatedly either a fixed
number of times or until some condition is reached.
while [ -n "$1" ]
do
sort "$1" | uniq
shift
done
The while loop continues to execute the lines between do and done as long
as the condition specified is true. Each time the loop executes the value of
$1 changes since the shift command is executed. In the example above, the
condition is [ -n "$1" ] which is true if $1 is any non-empty string and false
if $1 is the empty string (see help test).
6
8.2 The for Loop
Let us rewrite the example above using the for loop. Before that we need to
introduce the $@ shell built-in variable. When $@ appears in a script, it expands
to all the command line arguments. For example if we run the command
./uniquefruits.sh fruits.txt vegetables.txt extras.txt
$@ will expand to (be replaced by the shell with) fruits.txt vegetables.txt
extras.txt. Our example using the for loop will look like
#!/bin/bash