Chapter 7 Shell Programming
Chapter 7 Shell Programming
SHELL PROGRAMMING
AUTOMATING TASKS:
Agenda
4
UNIX Command Interpreters
bash is not only an excellent command line shell, but a scripting language in itself. Shell
scripting allows us to use the shell's abilities and to automate a lot of tasks that would
otherwise require a lot of commands.
Programming languages are generally a lot more powerful and a lot faster than
scripting languages. Programming languages generally start from source code and are
compiled into an executable. This executable is not easily ported into different operating
systems.
A scripting language also starts from source code, but is not compiled into an
executable. Rather, an interpreter reads the instructions in the source file and executes
each instruction. Interpreted programs are generally slower than compiled programs.
The main advantage is that you can easily port the source file to any operating system.
bash is a scripting language. Other examples of scripting languages are Perl, Lisp, and
Tcl.
What is Shell Script
Normally shells are interactive. It means shell accepts
commands from you (via keyboard) and execute them.
7
Shell Program Structure
8
Steps to Create Shell Programs
10
Basic Shell Programming
A script is a file that contains
shell commands
data structure: variables
control structure: sequence, decision, loop
Input
prompting user
command line arguments
Decision:
if-then-else
case
Repetition
do-while, repeat-until
for
select 11
What is a Shell Script? What To Do
vi hello.sh
#!/bin/sh
echo ‘Hello, world’
% chmod +x hello.sh
% ./hello.sh
Hello, world
What is a Shell Script? Executable
vi hello.sh
#!/bin/sh
echo ‘Hello, world’
% chmod +x hello.sh
% ./hello.sh
Hello, world
What is a Shell Script? Running it
vi hello.sh
#!/bin/sh
echo ‘Hello, world’
% chmod +x hello.sh
% ./hello.sh
Hello, world
FORMATTING SHELL PROGRAMS
Comments
Start comment lines with a pound sign (#)
Include comments to describe sections of your program
Help you understand your program when you look at it
later
15
Single and Double Quote
When assigning character data containing spaces or special characters, the data
must be enclosed in either single or double quotes.
Using double quotes to show a string of characters will allow any variables in the
quotes to be resolved
$ var=“test string”
$ newvar=“Value of var is $var”
$ echo $newvar
Value of var is test string
Using single quotes to show a string of characters will not allow variable
resolution
$ var=’test string’
$ newvar=’Value of var is $var’
Command Substitution
The backquote “`” is different from the single quote “´”. It is used for command substitution:
`command`
$ LIST=`ls`
$ echo $LIST
hello.sh read.sh
$ LIST=$(ls)
$ echo $LIST
hello.sh read.sh
$ x=hello
$ bash # Run a child shell.
$ echo $x # Nothing in x.
$ exit # Return to parent.
$ export x
$ bash
$ echo $x
hello # It's there.
If the child modifies x, it will not modify the parent’s original value. Ver ify this by
changing x in the following way:
The Environment
19
The Environment
The PATH : is a variable that instructs the shell about the route it should
follow to locate any executable command.
The HOME : when you log in, UNIX normally places you in a directory
named after your login name.
The SHELL: determines the type of shell that a user sees on logging in.
.bash_profile : the script executed during login time. Every time you
make changes to it, you should log out and log in again.
Aliases are listed when the alias statement is used without argument.
For ex.
– $ alias showdir=‘cd $1 ; ls -l’
When you want to see the contents of the directory /home/arm
21
Environmental Variables
There are two types of variables:
Local variables
Environmental variables
Environmental variables are set by the system and can usually be found by using the env
command. Environmental variables hold special values. For instance:
$ echo $SHELL
/bin/bash
$ echo $PATH
/usr/local/bin:/bin:/usr/bin
$ ./command
By setting PATH=$PATH:. our working directory is included in the search path for
commands, and we simply type:
$ command
If we type in
$ mkdir ~/bin
Read command
The read command allows you to prompt for input and store it in a variable.
Example:
#!/bin/bash
echo -n “Enter name of file to delete: ”
read file
echo “Type 'y' to remove it, 'n' to change your mind ... ”
rm -i $file
echo "That was YOUR decision!”
Line 2 prompts for a string that is read in line 3. Line 4 uses the interactive
remove (rm -i) to ask the user for confirmation.
Variables
We can use variables as in any programming languages. Their values are
always stored as strings, but there are mathematical operators in the shell
language that will convert variables to numbers for calculations.
We have no need to declare a variable, just assigning a value to its reference
will create it.
Example
#!/bin/bash
STR=“Hello World!”
echo $STR
Line 2 creates a variable called STR and assigns the string "Hello World!" to it.
Then the value of this variable is retrieved by putting the '$' in at the beginning.
Warning !
The shell programming language does not type-cast its variables. This means
that a variable can hold number data or character data.
count=0
count=Sunday
Switching the TYPE of a variable can lead to confusion for the writer of the script
or someone trying to modify it, so it is recommended to use a variable for only a
single TYPE of data in a script.
Examples: Command Line Arguments
27
Arithmetic Evaluation
The let statement can be used to do mathematical functions:
$ let X=10+2*7
$ echo $X
24
$ let Y=X+2*4
$ echo $Y
32
$ echo “$((123+20))”
143
$ VALORE=$[123+20]
Arithmetic Evaluation
Available operators: +, -, /, *, %
Example
$ cat arithmetic.sh
#!/bin/bash
echo -n “Enter the first number: ”; read x
echo -n “Enter the second number: ”; read y
add=$(($x + $y))
sub=$(($x - $y))
mul=$(($x * $y))
div=$(($x / $y))
mod=$(($x % $y))
if [ expression ];
then
statements
elif [ expression ];
then
statements
else
statements
fi
String Comparisons:
Examples:
Examples:
33
Examples
$ cat user.sh
#!/bin/bash
echo -n “Enter your login name: "
read name
if [ “$name” = “$USER” ];
then
echo “Hello, $name. How are you today ?”
else
echo “You are not $USER, so who are you ?”
fi
$ cat number.sh
#!/bin/bash
echo -n “Enter a number 1 < x < 10: "
read num
if [ “$num” -lt 10 ]; then
if [ “$num” -gt 1 ]; then
Expressions
Files operators:
Examples:
Exercise.
Example:
#!/bin/bash
echo -n “Enter a number 1 < x < 10:”
read num
if [ “$num” -gt 1 -a “$num” -lt 10 ];
then
echo “$num*$num=$(($num*$num))”
Bash Shell
Expressions
Logical operators:
Example:
#!/bin/bash
echo -n "Enter a number 1 < x < 10: "
read num
if [ “$number” -gt 1 ] && [ “$number” -lt 10 ];
then
echo “$num*$num=$(($num*$num))”
else
Example
$ cat iftrue.sh
#!/bin/bash
echo “Enter a path: ”; read x
if cd $x; then
echo “I am in $x and it contains”; ls
else
echo “The directory $x does not exist”;
exit 1
fi
Shell Parameters
Positional parameters are assigned from the shell’s argument when it is invoked.
Positional parameter “N” may be referenced as “${N}”, or as “$N” when “N”
consists of a single digit.
Special parameters
$ cat sparameters.sh
#!/bin/bash
Trash
$ cat trash.sh
#!/bin/bash
if [ $# -eq 1 ];
then
if [ ! –d “$HOME/trash” ];
then
mkdir “$HOME/trash”
fi
mv $1 “$HOME/trash”
else
echo “Use: $0 filename”
exit 1
fi
Case Statement
Used to execute statements based on specific values. Often used in place of an
if statement if there are a large number of conditions.
case $var in
val1)
statements;;
val2)
statements;;
*)
statements;;
Example 1 (case.sh)
$ cat case.sh
#!/bin/bash
echo -n “Enter a number 1 < x < 10: ”
read x
case $x in
1) echo “Value of x is 1.”;;
2) echo “Value of x is 2.”;;
3) echo “Value of x is 3.”;;
4) echo “Value of x is 4.”;;
5) echo “Value of x is 5.”;;
6) echo “Value of x is 6.”;;
7) echo “Value of x is 7.”;;
8) echo “Value of x is 8.”;;
9) echo “Value of x is 9.”;;
Example 2: The case Statement
#!/bin/bash
echo "Enter Y to see all files including hidden files"
echo "Enter N to see all non-hidden files"
echo "Enter q to quit"
case $reply in
Y|YES) echo "Displaying all (really…) files"
ls -a ;;
N|NO) echo "Display all non-hidden files..."
ls ;;
Q) exit 0 ;;
#!/bin/bash
ChildRate=3
AdultRate=10
SeniorRate=7
read -p "Enter your age: " age
case $age in
[1-9]|[1][0-2]) # child, if age 12 and younger
echo "your rate is" '$'"$ChildRate.00" ;;
# adult, if age is between 13 and 59 inclusive
[1][3-9]|[2-5][0-9])
echo "your rate is" '$'"$AdultRate.00" ;;
[6-9][0-9]) # senior, if age is 60+
echo "your rate is" '$'"$SeniorRate.00" ;;
esac
46
Repetition Constructs
47
Iteration Statements
The for structure is used when you are looping through a range of variables.
statements are executed with var set to each value in the list.
Example
#!/bin/bash
let sum=0
for num in 1 2 3 4 5
Iteration Statements
#!/bin/bash
for x in paper pencil pen
do
echo “The value of variable x is: $x”
sleep 1
done
if the list part is left off, var is set to each parameter passed to the script ( $1, $2,
$3,…)
$ cat for1.sh
#!/bin/bash
for x
do
echo “The value of variable x is: $x”
sleep 1
Example (old.sh)
$ cat old.sh
#!/bin/bash
# Move the command line arg files to old directory.
if [ $# -eq 0 ] #check for command line arguments
then
echo “Usage: $0 file …”
exit 1
fi
if [ ! –d “$HOME/old” ]
then
mkdir “$HOME/old”
fi
echo The following files will be saved in the old directory:
echo $*
for file in $* #loop through all command line arguments
Example (args.sh)
$ cat args.sh
#!/bin/bash
# Invoke this script with several arguments: “one two three“
if [ ! -n “$1” ]; then
echo “Usage: $0 arg1 arg2 ..." ; exit 1
fi
echo ; index=1 ;
echo “Listing args with \”\$*\”:”
for arg in “$*” ;
do
echo “Arg $index = $arg”
let “index+=1” # increase variable index by one
done
echo “Entire arg list seen as single word.”
echo ; index=1 ;
echo “Listing args with \”\$@\”:”
Logic: for
for i in /*
do
echo “Listing $i:”
ls -l $i
read
done
Example 2: Using the for Loop
#!/bin/bash
# compute the average weekly temperature
for num in 1 2 3 4 5 6 7
do
read -p "Enter temp for day $num: " Temp
let TempTotal=$TempTotal+$Temp
done
let AvgTemp=$TempTotal/7
echo "Average temperature: " $AvgTemp
53
A C-like for loop
An alternative form of the for structure is
$ cat for2.sh
#!/bin/bash
echo –n “Enter a number: ”; read x
let sum=0
for (( i=1 ; $i<$x ; i=$i+1 )) ; do
While Statements
The while structure is a looping structure. Used to execute a set of commands
while a specified condition is true. The loop terminates as soon as the condition
becomes false. If condition never becomes false, loop will never exit.
while expression
do
statements
done
$ cat while.sh
#!/bin/bash
echo –n “Enter a number: ”; read x
let sum=0; let i=1
while [ $i –le $x ]; do
let “sum = $sum + $i”
Example: Using the while Loop
#!/bin/bash
COUNTER=0
while [ $COUNTER -lt 10 ]
do
echo The counter is $COUNTER
let COUNTER=$COUNTER+1
done
56
Menu
$ cat menu.sh
#!/bin/bash
clear ; loop=y
while [ “$loop” = y ] ;
do
echo “Menu”; echo “====”
echo “D: print the date”
echo “W: print the users who are currently log on.”
echo “P: print the working directory”
echo “Q: quit.”
echo
read –s choice # silent mode: no echo to terminal
case $choice in
D | d) date ;;
W | w) who ;;
P | p) pwd ;;
Find a Pattern and Edit
$ cat grepedit.sh
#!/bin/bash
# Edit argument files $2 ..., that contain pattern $1
if [ $# -le 1 ]
then
echo “Usage: $0 pattern file …” ; exit 1
else
pattern=$1 # Save original $1
shift # shift the positional parameter to the left by 1
while [ $# -gt 0 ] # New $1 is first filename
do
grep “$pattern” $1 > /dev/null
if [ $? -eq 0 ] ; then # If grep found pattern
vi $1 # then vi the file
fi
Example: Using the while Loop
#!/bin/bash
# copies files from home- into the webserver- directory
# A new directory is created every hour
PICSDIR=/home/carol/pics
WEBDIR=/var/www/carol/webcam
while true; do
DATE=`date +%Y%m%d`
HOUR=`date +%H`
mkdir $WEBDIR/"$DATE"
while [ $HOUR -ne "00" ]; do
DESTDIR=$WEBDIR/"$DATE"/"$HOUR"
mkdir "$DESTDIR"
59
Continue Statements
The continue command causes a jump to the next iteration of the loop, skipping
all the remaining commands in that particular loop cycle.
$ cat continue.sh
#!/bin/bash
LIMIT=19
echo
echo “Printing Numbers 1 through 20 (but not 3 and 11)”
a=0
while [ $a -le “$LIMIT” ]; do
a=$(($a+1))
if [ “$a” -eq 3 ] || [ “$a” -eq 11 ]
then
continue
The continue
while [ condition ]
do
cmd-1
continue
This iteration is
over; do the next
cmd-n
iteration
done
echo "done"
61
Break Statements
The break command terminates the loop (breaks out of it).
$ cat break.sh
#!/bin/bash
LIMIT=19
echo
echo “Printing Numbers 1 through 20, but something happens after 2 … ”
a=0
while [ $a -le “$LIMIT” ]
do
a=$(($a+1))
if [ “$a” -gt 2 ]
then
break
fi
The break
while [ condition ]
do
cmd-1
break
This iteration is over
cmd-n
and there are no more
done iterations
echo "done"
63
Example:
for index in 1 2 3 4 5 6 7 8 9 10
do
if [ $index –le 3 ]; then
echo "continue"
continue
fi
echo $index
if [ $index –ge 8 ]; then
echo "break"
break
fi
done
64
Until Statements
The until structure is very similar to the while structure. The until structure loops
until the condition is true. So basically it is “until this condition is true, do this”.
until [expression]
do
statements
done
$ cat countdown.sh
#!/bin/bash
echo “Enter a number: ”; read x
echo ; echo Count Down
until [ “$x” -le 0 ]; do
echo $x
Example: Using the until Loop
#!/bin/bash
Stop="N"
until [ $Stop = "Y" ]; do
ps -A
read -p "want to stop? (Y/N)" reply
Stop=`echo $reply | tr [:lower:] [:upper:]`
done
echo "done"
66
Manipulating Strings
Bash supports a number of string manipulation operations.
Example
$ st=0123456789
$ echo ${#st}
10
$ echo ${st:6}
6789
Parameter Substitution
Manipulating and/or expanding variables
$ echo ${username-`whoami`}
alice
$ username=bob
$ echo ${username-`whoami`}
bob
$ unset username
$ echo ${username=`whoami`}
$ echo $username
alice
Parameter Substitution
${parameter?msg}, if parameter set, use it, else print msg
Example
#!/bin/bash
OUTFILE=symlinks.list # save file
directory=${1-`pwd`}
for file in “$( find $directory -type l )”
Functions
Functions make scripts easier to maintain. Basically it breaks up the program
into smaller pieces. A function performs an action defined by you, and it can
return a value if you wish.
#!/bin/bash
hello()
{
echo “You are in function hello()”
}
In the above, we called the hello() function by name by using the line: hello .
When this line is executed, bash searches the script for the line hello(). It finds it
right at the top, and executes its contents.
Functions
Syntax:
function-name () {
statements
} 71
Example: function
#!/bin/bash
funky () {
# This is a simple function
echo "This is a funky function."
echo "Now exiting funky function."
}
funky
72
Functions
$ cat function.sh
#!/bin/bash
function check() {
if [ -e "/home/$1" ]
then
return 0
else
return 1
fi
}
echo “Enter the name of the file: ” ; read x
if check $x
then
echo “$x exists !”
Example: Picking a random card from a deck
#!/bin/bash
# Count how many elements.
77
Example: function with parameter
#! /bin/sh
testfile() {
if [ $# -gt 0 ]; then
if [[ -f $1 && -r $1 ]]; then
echo $1 is a readable file
else
echo $1 is not a readable file
fi
fi
}
testfile .
testfile funtest 78
Example: function with parameters
#! /bin/bash
checkfile() {
for file
do
if [ -f "$file" ]; then
echo "$file is a file"
else
if [ -d "$file" ]; then
echo "$file is a directory"
fi
fi
done
} 79
Local Variables in Functions
80
Example: function
#! /bin/bash
foo () {
local inside="not so good variable"
echo $global
echo $inside
global="better variable"
}
echo $global
foo 81
Example: function
#! /bin/bash
foo () {
local inside="not so good variable"
echo $global
echo $inside
global="better variable"
}
echo $global
foo 82
Using Arrays with Loops
In the bash shell, we may use arrays. The simplest way to create one is using one of the
two subscripts:
pet[0]=dog
pet[1]=cat
pet[2]=fish
pet=(dog cat fish)
$ echo ${pet[0]}
dog
echo ${arrayname[*]}
Debugging
Bash provides two options which will give useful information for debugging
-x : displays each line of the script with variable substitution and before execution
-v : displays each line of the script as typed before execution
Usage:
$ cat for3.sh
#!/bin/bash –x
echo –n “Enter a number: ”; read x
let sum=0
for (( i=1 ; $i<$x ; i=$i+1 )) ; do
Debugging
$ for3.sh
+ echo –n ‘Enter a number: ’
Enter a number: + read x
3
+ let sum=0
+ (( i=0 ))
+ (( 0<=3 ))
+ let ‘sum = 0 + 0’
+ (( i=0+1 ))
+ (( 1<=3 ))
+ let ‘sum = 0 + 1’
+ (( i=1+1 ))
+ (( 2<=3 ))
+ let ‘sum = 1 + 2’
+ (( i=2+1 ))
+ (( 3<=3 ))
Example: Suite drawing statistics
$ cat cardstats.sh
#!/bin/sh # -xv
N=100000
hits=(0 0 0 0) # initialize hit counters
if [ $# -gt 0 ]; then # check whether there is an argument
N=$1
else # ask for the number if no argument
echo "Enter the number of trials: "
TMOUT=5 # 5 seconds to give the input
read N
fi
i=$N
echo "Generating $N random numbers... please wait."
SECONDS=0 # here is where we really start
while [ $i -gt 0 ]; do # run until the counter gets to zero
case $((RANDOM%4)) in # randmize from 0 to 3
0) let "hits[0]+=1";; # count the hits
1) let "hits[1]=${hits[1]}+1";;
2) let hits[2]=$((${hits[2]}+1));;
Challenge/Project: collect
Write a utility to collect “well-known” files into convenient directory holders.
collect <directory>*
The utility should collect all executables, libraries, sources and includes from each
directory given on the command line or entered by the user (if no arguments were passed)
into separate directories. By default, the allocation is as follows:
– executables go to ~/bin
– libraries (lib*.*) go to ~/lib
– sources (*.c, *.cc, *.cpp, *.cxx) go to ~/src
– includes (*.h, *.hxx) go to ~/inc
The utility should ask whether another directory should be used in place of these default
directories.
Each move should be recorded in a log file that may be used to reverse the moves (extra
points for writing a reverse utility!). The user should have an option to use a log file other
than the default (~/organize.log).
At the end, the utility should print statistics on file allocation: how many directories were