0% found this document useful (0 votes)
86 views

5 - CSC350 - Shell Scripting Using BASH

This document provides an outline of topics for shell scripting using BASH. It introduces variables, command shortcuts, tilde substitution, redirection, command substitution, arithmetic, conditional expressions, control structures, functions, and menus. It discusses how BASH is the default shell for Linux users and attempts to provide useful features from other shells. It also covers variable manipulation, shell commands, startup files, accessing variable values, and some examples of using variables.

Uploaded by

Abass Hodroj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
86 views

5 - CSC350 - Shell Scripting Using BASH

This document provides an outline of topics for shell scripting using BASH. It introduces variables, command shortcuts, tilde substitution, redirection, command substitution, arithmetic, conditional expressions, control structures, functions, and menus. It discusses how BASH is the default shell for Linux users and attempts to provide useful features from other shells. It also covers variable manipulation, shell commands, startup files, accessing variable values, and some examples of using variables.

Uploaded by

Abass Hodroj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 124

Chapter 5: Shell Scripting using BASH

CSC350 UNIX Programming


Dr. Maroun Abi Assaf

1
Outline
• Introduction
• Variables
• Command Shortcuts
• Tilde Substitution
• Redirection
• Command Substitution
• Arithmetic
• Conditional Expressions
• Control Structures
• F
Functions
i
• Menus: select

2
Introduction

3
Motivation
• Bash is
Bash is
– the default shell for Linux users. 
– an attempt to create a 
an attempt to create a "best
best of all shells
of all shells" 
• provides the most useful features of both the C shell and 
Korn shell. 
o s e
– compatible with the original UNIX Bourne shell
– an open software
an open software product, 
product,
– freely available and can be found in all Linux distributions, 
• can
can be downloaded and installed on just about any version 
be downloaded and installed on just about any version
of UNIX if it isn't already present.

4
Shell Commands
• alias • kill
• bg • local
• builtin • popd
• case..in..esac
i • pushdhd
• cd • read
• declare • readonly
• dirs • return
• env • select..do..done
• export • set
• fg • source
• for..do..done • trap
• function • unalias
• history • unset
• if..then..elif..then..else..fi • until..do..done
• jobs • while do done
while..do..done
5
Introduction
• On
On Linux systems, Bash is installed as /bin/bash, but 
Linux systems Bash is installed as /bin/bash but
/bin/sh is a pointer to /bin/bash, 
• The following features of Bash that are new or a bit 
The following features of Bash that are new or a bit
different from what has been discussed in previous 
chapters include:
chapters include:
– variable manipulation
– arithmetic, conditional expressions, control structures
ih i di i l i l
– shell functions

6
Startup
• Bash is a program. 
Bash is a program.
• When a new Bash shell starts, 
– it executes commands in the file ".bashrc" in the home directory of 
y
the user running Bash. 
– The one exception is when Bash is started as a login shell, in which 
case it runs the commands in the file " bash profile" in the user's
case it runs the commands in the file ".bash_profile" in the user's 
home directory instead. 
– So if you want your ".bashrc" file to be executed in your login shell as 
well, you have to add this to your ".bash_profile" file:
if [ -f ~/.bashrc ]; then
.~/.bashrc
/ b h
fi

7
Startup
• We'll
We ll see how and why that works later in the 
see how and why that works later in the
chapter. 
• This is often found by default in ".bash_profile" files 
This is often found by default in " bash profile" files
on a system.
• In addition to these two files, 
dd h fl
– the system administrator can put initialization commands 
appropriate for all users in the file "/etc/profile" 
i f ll i h fil "/ / fil "
which Bash will also read and execute. 

8
Variables

9
Variables
• Bash allows creation and use of shell variables for 
Bash allows creation and use of shell variables for
the following purposes:
1.
1 Value assignment
Value assignment and access
and access
2. Defining and using lists of values [array] 
3
3. Testing a value or for existence of a variable
a value or for existence of a variable
4. Reading or writing a variable's value

10
Creating/Assigning a Simple
Variable
• To assign a value to a simple variable, the syntax is similar to 
g p , y
that of other shells.
• Example of assigning a simple variable.
{name=value}+
– If a variable doesn't exist, it is implicitly created; 
– otherwise, its previous value is overwritten. 
– A newly created variable is always local, 
• although we may turn it into an environment variable. 
• To assign a value that contains spaces, surround the value by 
quotes. 
• Here are some examples:
$ teamname="Denver
teamname= Denver Broncos"
Broncos
$ gameswon=12
$ gameslost=3
11
Accessing Simple Variables
Syntax
y Action
$name Replaced by the value of name.
${name} Replaced
p byy the value of name. This form is useful if the expression
p is
immediately followed by an alphanumeric that would otherwise be
interpreted as part of the variable name.
${name-word}
${ } Replaced by the value of name if set, and word otherwise.
${name+word} Replaced by word if name is set, and nothing otherwise.
${name=word} Assigns word to the variable name if name is not already set,
set and then
is replaced by the value of name.
${name?word} Replaced by name if name is set. If name is not set, word is displayed
to the
h standard
d d error channel
h l andd the
h shell
h ll is
i exited.
i d If wordd is
i
omitted, then a standard error message is displayed instead.
${#name} Replaced by the length of the value of name.
${#name[*] } Replaced by the number of elements in the array name. 12
Accessing Simple Variables
Syntax
y Action

${name:+word } Work like their counterparts that do not contain a :, except


${name:=word } that name must be set and non-null, instead of just set.
${name:?word }
${name:+word }
${name#pattern}
p Removes a leadingg pattern
p from name. The expression
p is
${name##pattern} replaced by the value of name if name doesn't begin
with pattern, and with the remaining suffix if it does.
The first form removes the smallest matching pattern,
and the second form removes the largest matching
pattern [using wildcards].
${
${name%pattern}
p } Removes a trailing pattern from name. The expression is
${name%%pattern} replaced by the value of name if name doesn't end with
pattern, and with the remaining prefix if it does. The
first form removes the smallest matchingg pattern,
p , and
the second form removes the largest matching pattern
[using wildcards]. 13
• In the first example, 
– I used braces to append a string to the value of a variable:
pp g
$ verb=sing ...assign a variable.
$ echo I like $verbing ...there's no
variable
i bl " "verbing".
bi "
I like
$ echo I like ${verb}ing ...now it works.
I like singing
$_ _
• Here's an example that 
– uses command substitution to set the variable startDate to the 
y
current date if it's not already set:
$ startDate=${startDate-`date`} ...if not
set, run date.
$ echo $startDate ...look
look at its value
value.
Wed May 4 06:56:51 CST 2005
$ _
14
• In the next example, 
– I set the variable x to a default value and printed its value, all at the same 
p
time:
$ echo x = ${x=10} ...assign a default value.
x = 10
$ echo $x ...confirm the variable was set.
10
$ _
• In the following example, 
– I displayed messages based on whether certain variables were set or not:
$ flag=1 ...assign a variable.
$ echo ${flag+'flag is set'} ...conditional message
#1.
flag is set
$ echo ${flag2+'flag2 is set'} ...conditional
message #2.
#2
...result is null
$ _

15
• In the next example, 
– I tried to access an undefined variable called grandTotal and received an error 
message instead:
i t d
$ total=10 ...assign a variable.
$ value=${total?'total not set'} ...accessed OK.
$ echo $value ...look at its value.
10
$ value=${grandTotal?'grand total not set'} ...not
set.
grandTotal: grand total not set
$ _
• In this example, 
– I ran a script that used the same access method as the previous example. 
Note that the script terminated when the access error occurred:
$ cat script.sh ...look at the script.
value=${grandTotal?'grand total is not set'}
echo done # this line is never executed.
$ ./script.sh ...run the script.
script.sh: grandTotal: grand total is not set
$ _
16
• In this final example, 
– I'll modify some variable values:
$ echo $PWD ...display the current working directory.
/home/glass/dir1
$ echo $HOME
/home/glass
$ echo $
${PWD#$HOME/}...remove
#$ / leading $HOME/
$ /
dir1
$ fileName=menu.sh ...set a variable.
$ echo ${fileName%.sh}.bak ...remove trailing ".sh"
menu.bak ...and add ".bak".
$ x=/one/two/three
$ echo ${x##*}

$_
$ echo ${x##*/}
three
$ echo ${x#*/}
one/two/three 17
Creating/Assigning a List Variable
• declare create
declare create a list of variables, or arrays
a list of variables or arrays
– although simply using a variable in the form of an array 
will also work.
will also work.
• Shell command: declare [-axir] [listname]
– If the named variable does not already exist, it is created. 
If the named variable does not already exist it is created
– If an array name is not specified when -a is used, 
• declare will display
will display all currently defined arrays and their 
all currently defined arrays and their
values. 
– If
If the 
the -x
x option is used, the variable is exported
option is used, the variable is exported to 
to
subshells. 
– If the –i option is used, the variable is considered integer
p , g
– If the –r option is used, makes the variable read only 18
Creating/Assigning a List Variable
• Here
Here is an example of how you might create a list of 
is an example of how you might create a list of
teams:
$ declare -a teamnames
$ teamnames[0]="Dallas Cowboys"
$ teamnames[1]="Washington Redskins"
$ teamnames[2]="New York Giants"
• IIn reality, if you omit the declare
lit if it th d l command, the 
d th
other lines will still work as expected.

19
Accessing List Variables
• When accessing array values, 
When accessing array values
– you can always put braces around the variable name to 
explicitly distinguish it from other text that might be
explicitly distinguish it from other text that might be 
around it to prevent the shell from trying to use other text 
as part of the variable name.

${name[index]} Access the indexth element of the array $name.


$name
${name[*]} or Access all elements of the array $name.
${name[@]}
${#name[*]} or Access the number of elements in the array
${#name[@]} $name.

20
Accessing List Variables
• The
The braces are required when using arrays to 
braces are required when using arrays to
distinguish between other shell operators. 
– Suppose
Suppose, for example, we have our list of 32 NFL teams 
for example we have our list of 32 NFL teams
stored as $teamname[0] .. $teamname[31]. 
– One might use this information this way:
One might use this information this way:
$ echo "There are ${#teamnames[*]} teams in the NFL"
There are 32 teams in the NFL
$ echo "They are: ${teamnames[*]}"
...

21
Building Lists
• You can build an array or list variable in one of two ways. 
y y
1. If you know how many elements you will need, 
– you can use the declare built‐in command to define the space 
g p
and assign the values into specific locations in the list. 
2. If you don't know, or don't care, how many elements will be in the 
list, 
– you can simply list them and they will be added in the order you 
y py y y
specify.
• For example, 
– to define our list of NFL teams, of which we know (at least today) 
to define our list of NFL teams, of which we know (at least today)
there are 32, you might define it as follows:
$ declare -a teamnames
$ teamnames[0]="Dallas Cowboys"
$ teamnames[1]="Washington Redskins"
$ tea
teamnames[2]="New
a es[ ] e York
o G
Giants"
a ts
... 22
$ teamnames[31]="Houston Texans"
Building Lists
• This can also be done in a single long command:
$ declare -a teamnames
$ teamnames=([0]="Dallas Cowboys" \
[1]="Washington
[ ] g Redskins" \
...
[31]="Houston Texans")
• The backslash is used to tell the shell that the command is continued on the 
The backslash is used to tell the shell that the command is continued on the
next line.
• We could have also done it this way:
$ teamnames=("Dallas
teamnames ( Dallas Cowboys" Cowboys "Washington
Washington Redskins"
Redskins \
"New York Giants" "New York Jets" \
...
"Houston Texans")
• If an array tab contains numbers, we declare it in a similar way: 
tab=( 5 12 14 3.5)

23
Building Lists
• Note that if you have not assigned values in consecutive 
y g
locations
– but skipped around when you ask for the number of values in the array, 
– the value will be the actual number of populated elements, not the 
th l ill b th t l b f l t d l t t th
largest index defined. 
• For example:
p
$ mylist[0]=27
$ mylist[5]=30
$ echo ${#mylist[*]} ...number
n mbe of elements in m mylist[]
list[]
2
$ declare -a ...display defined element values
declare -a mylist='([0]="27" [5]="30")'
$ _

24
Destroying lists
• List
List variables can be deallocated or destroyed using the unset 
variables can be deallocated or destroyed using the unset
built‐in to Bash 
• Shell command: unset name
unset name[index]
– Deallocates the specified variable or element in the list variable.
• If you have finished using an array, you can deallocate the 
space used by the array by destroying it completely. 
• It is more likely you will want to remove a specific element in 
the array.
$ unset teamnames[17]

– Now our array contains 31 names instead of 32.
Now our array contains 31 names instead of 32
25
Reading a Variable from Standard
Input
• The read command allows you to read variables from standard input 
• Shell Command: read { variable }+
– read reads one line from standard input and then assigns successive words 
from the line to the specified variables
from the line to the specified variables.
– Any words that are left over are assigned to the last‐named variable.
• If you specify just one variable, the entire line is stored in the variable. 
• Here's an example script that prompts a user for his or her full name:
$ cat script.sh ...list the script.
echo -n "Please enter y your name: "
read name # read just one variable.
echo your name is $name # display the variable.
$ bash script.sh...run the script.
Please enter your name: Graham Walker Glass
your name is Graham Walker Glass ...whole line was
read.
$ _
26
• Here's an example that illustrates what happens when you 
specify more than one variable:
specify more than one variable:

$ cat
t script.sh
i t h ...list
li t th
the script.
i t
echo -n "Please enter your name: "
read firstName lastName # read two variables.
echo your first name is $firstName
echo your last name is $lastName
$ bash script.sh ...run the script.
Please enter your name: Graham Walker Glass
your first name is Graham ...first word.
your last name is Walker Glass ...the rest.
$ bash script.sh ...run it again.
Please enter your name: Graham
your first name is Graham ...first word.
your last name is
y ...onlyy one.
$ _

27
Exporting Variables
• In all shells, variables are local to the specific shell 
In all shells variables are local to the specific shell
– and, unless otherwise specified, are not passed to 
subshells. 
subshells
• Shell Command: export { variable }+
– export marks the specified variables for export to the 
marks the specified variables for export to the
environment. 
– If no variables are specified, a list of all the variables 
If no variables are specified a list of all the variables
marked for export during the shell session is displayed.

28
Exporting Variables
• Usually, we use uppercase letters to name environment 
y, pp
variables. 
• The env utility allows you to modify and list environment 
variables 
variables
• Utility: env { variable=value }* [ command ]
– env assigns values to specified environment variables, 
• and then executes an optional command using the new 
environment. 
– If variables or command are not specified, a list of the current 
environment is displayed.
i t i di l d
• In the following example, 
– I created a local variable called DATABASE, which I then marked for 
export. 
– When I created a subshell, a copy of the environment variable was 
inherited:

29
$ export ...list my current exports.
export TERM ...set in my ".profile" startup file.
$ DATABASE=/dbase/db ...create a local variable.
$ export DATABASE ...mark it for export.
$ export
p ...note that it's been added.
export DATABASE
export TERM
$ env ...list
list the environment
environment.
DATABASE=/dbase/db
HOME=/home/glass
LOGNAME glass
LOGNAME=glass
PATH=:/bin:/usr/bin
SHELL=/bin/bash
TERM=xterm
USER=glass
$ bash ...create a subshell.
$ echo $DATABASE ...a copy was inherited.
/dbase/db
$ ^D ...terminate subshell.
$ _
30
Exporting Variables
• Bash
Bash provides a shell option that allows you to 
provides a shell option that allows you to
specify that, by default, all shell variables be 
exported to any subshells created
exported to any subshells created. 
• Shell command: set -o allexport
– Tell the shell to export all
T ll h h ll ll variables to subshells.
i bl b h ll

31
Read-Only Variables
• The
The readonly
readonly command allows you to protect variables 
command allows you to protect variables
against modification 
• Shell Command: readonly y { variable } }*
– readonly makes the specified variables read‐only, 
• protecting them against future modification. 
– If no variables are specified, a list of the current read‐only variables is 
displayed. 
– Copies of exported variables do not inherit their read
Copies of exported variables do not inherit their read‐only
only status.
status
• In the following example, 
– I protected a local variable from modification. 
p
– I then exported the variable and showed that its copy did not inherit 
the read‐only status:

32
$ password=Shazam ...assign a local variable.
$ echo $password ...display its value.
Shazam
Sh
$ readonly password ...protect it.
$ readonly ...list all read-only variables.
readonly password
$ password=Phoombah ...try to modify it.
password: is read only
$ export password ...export
export the variable
variable.
$ password=Phoombah ...try to modify it.
password: is read only
$ bash ...create a subshell.
$ readonly ...the exported password is not read-only.
$ echo $password ...its value was copied correctly.
Shazam
$ password=Alacazar ...but its value may be changed.
$ echo $password ...echo its value.
Alacazar
$ ^D ...terminate the subshell.
$ echo $password ...echo original value.
Shazam
$ _
33
Predefined Variables
• Like most shells, Bash defines some variables when it starts 
Like most shells, Bash defines some variables when it starts
Name Value
$-
$ The current shell options assigned from the command line or
by the built-in set command discussed later.
$$ The process ID of this shell.
$! The process ID of the last background command.
$# The number of positional parameters.
$? The exit value of the last command.
command
$@ An individually quoted list of all the positional parameters.
$_ The last parameter of the previous command.
$BASH The full pathname of the Bash executable.
$BASH_ENV Location of Bash's startup file (default is ~/.bashrc).
$
$BASH_VERSINFO A read-only array of version information.
$BASH_VERSION Version string. 34
$DIRSTACK Array defining the directory stack (discussed later).
$ENV If this variable is not set, the shell searches the user's home
directory for the ".profile" startup file when a new login shell
is created. If this variable is set, then every new shell
invocation runs the script specified by ENV.
$EUID Read-only value of effective user ID of user running Bash.
$HISTFILE Location of file containing shell history (default ~/.bash_history).
$HISTFILESIZE Maximum number of lines allowed in history file (default is 500).

$HISTSIZE Maximum number of commands in history (default is 500).


$HOSTNAME Hostname of machine where Bash is running.
$HOSTTYPE Type of host where Bash is running.
$IFS When the shell tokenizes a command line prior to its execution, it
uses the characters in this variable as delimiters. IFS usually
contains a space,
space a tab,
tab and a newline character.
character
$LINES Used by select to determine how to display the selections.
$MAILCHECK How often (seconds) to check for new mail.
$OLDPWD The previous working directory of the shell. 35
$OSTYPE Operating system of machine where Bash is running.
$PPID The process ID number of the shell's parent.

$PPID Read-only process ID of the parent process of Bash.

$PS1 This contains the value of the command-line prompt, and is $ by


default. To change the command-line prompt, simply set PS1 to a
new value.

$PS2 This contains


Thi t i the
th value
l off the
th secondary
d command-line
d li promptt that
th t is
i
displayed when more input is required by the shell, and is > by
default. To change the prompt, set PS2 to a new value.

$PS3 The prompt used by the select command, #? by default.

$PWD The current workingg directoryy of the shell.

$RANDOM A random integer.

$REPLY S t bby a select


Set l t command.
d

$SHLVL Level of shell (incremented once each time a Bash process is started,
it shows how deeply the shell is nested)
nested).
$UID Read-only value of user ID of user running Bash. 36
Predefined Variables
• In this example, we illustrate the first three variables. 
– the C compiler (gcc) was invoked on a file that didn't exist, and therefore 
returned a failure exit code.
$ cat script.sh ...list the script.
echo there are $# command line arguments: $@
gcc $1 # compile the first argument.
echo the last exit value was $? # display
exit code.
$ ./script.sh nofile tmpfile ...execute the
script.
script
there are 2 command line arguments: nofile
tmpfile
gcc: nofile: No such file or directory
gcc: no input files
the last exit value was 1 ...gcc
errored
errored.
$ _ 37
Predefined Variables
• The
The next example illustrates how $!
next example illustrates how $! may be used to kill 
may be used to kill
the last background process:
$ sleep 1000 & ...bg process, bash does not report PID.
$ kill $! ...kill it!
29455 Terminated
$ echo $! ...the process ID is still remembered.
29455
$ _

38
Predefined Variables
• Here's a small example that illustrates the predefined variables used to 
set the command‐line prompt. 
– I set my prompt to something different by assigning a new value to PS1, and I 
set PS2 to a new value and illustrated an occasion where the secondary 
y
prompt is displayed.

$ PS1="bash? "...set a new p


primaryy p
prompt.
p
bash? string="a long\...assign a string over 2 lines
> string" ...">" is the secondary prompt.
g ...look at the value of "string".
bash? echo $string
a long string
bash? PS2="??? " ...change the secondary prompt.
bash? string="a long\ ...assign a long string.
??? string" ..."???" is new secondary prompt.
bash? echo $string ...look at the value of "string".
a long string
b h _
bash?
39
Command Shortcuts

Bash provides a few ways to shorten 
p y
commands and arguments you type at the 
keyboard. 
y

40
Aliases
• Bash
Bash allows you to define your own commands with 
allows you to define your own commands with
the alias built‐in command 
• Shell Command: alias
Shell Command: alias [word[=string]]
– If you alias a new command word equal to string, 
• th
then when you type the command word, the string will be 
h t th d d th t i ill b
used in its place (and any succeeding arguments will be 
appended to string) and the command will be evaluated. 
pp g)
– In the usage "alias word" any alias defined for word will be 
printed. 
– Its simplest usage "alias" will print all defined aliases.
– there is also a –p argument that can be used 
41
Aliases
• Here
Here is an example of defining and using Bash 
is an example of defining and using Bash
command aliases:
$ alias dir="ls
dir ls -aF"
aF
$ dir
./ main2.c p.reverse.c reverse.h
../ main2.o palindrome.c reverse.old
$ dir *.c
main2.c
i 2 p.reverse.c palindrome.c
li d
$

42
Aliases
• To
To cause an alias to no longer have a special 
cause an alias to no longer have a special
definition, use the unalias built‐in 
• Shell Command: unalias
Shell Command: unalias [ [-a]a] {word}+
– Remove the specified alias(es). 
– If "‐a" is used, remove all aliases.
If " " i d ll li

• YYou might wish to undefine an alias when you want 
i ht i h t d fi li h t
the normal behavior of a command that you 
normally alias to a different behavior
ll li t diff tb h i

43
Command History
• Commands you have typed to the shell are stored in a history 
y yp y
file defined by the $HISTFILE shell variable. 
• By default, the value specifies the file ".bash_history" in the 
user's home directory. 
' h di
• This file can hold a maximum of $HISTFILESIZE entries, the 
default value being 500
default value being 500.
Reading Command History 
• To see your shell history, use the built
To see your shell history, use the built‐in
in history command 
history command
• Shell Command: history [-c] [n]
– Print out the shell's current command history. If a numeric value n is 
specified, show only the last n entries in the history list. If "‐c" is used, 
clear the history list.
• Other options of the command history exists…
p y
44
Tilde Substitution
• Any token of the form ~name is subject to tilde substitution. 
y j
• The shell checks the password file to see if name is a valid 
user name, and if it is, replaces the ~name sequence with the 
f ll
full pathname of the user's home directory. 
h f h ' h di
• If it isn't, the ~name sequence is left unchanged. 

Tilde sequence Replaced by


~ $HOME
~user home directory of user
~/pathname $HOME/pathname
~+ $PWD (current working directory)
~- $OLDPWD ((previous
i working
ki directory)
di t )
45
Tilde Substitution
• Here are some examples of tilde substitution:

$ pwd
g
/home/glass ...current working
g directory.
y
$ echo ~
/home/glass ...my home directory.
$ cd / ...change to root directory.
$ echo ~+
/ ...current working directory.
$ echo ~-
/home/glass ...previous working directory.
$ echo ~dcox
/home/dcox ...another user's home directory.
$ _

46
Command Substitution
• In
In addition to the older method of command substitution 
addition to the older method of command substitution
surrounding the command with grave accents 
– Bash allows you to perform command substitution using the syntax 
$( command )
• Note that the $ that immediately precedes the open 
parentheses is part of the syntax, and is not a prompt. 
• Here's an example:
$ echo there are $(who | wc -l) users on the system
there are 6 users on the system
$ _

47
Arithmetic for integers
• To
To perform an arithmetic operation in Bash, you simply put 
perform an arithmetic operation in Bash, you simply put
the operation inside a double set of parentheses 
(( operation
p )) E.g.: x=$(( 1+3 )) ; echo $x
E.g.: x=$[1+3]; echo $x
$(( expression )) E.g.: if(($x>10)); then …
E.g.: ((x++))
$[ expression
p ] E
E.g.:((x=x+1));
(( 1)) x=$(($x+1));
$(($ 1))
E.g.: x=$((x+1));
E.g.: x=$[$x+1]; x=$[x+1];

Common numeric operations include those listed


+ - Addition, subtraction.
++ -- Increment, decrement.
* / % Multiplication, division, remainder.
** Exponentiation. 48
Arithmetic
• Integer
Integer arithmetic is faster than floating‐point 
arithmetic is faster than floating‐point
arithmetic. 
• If you know your variable will always be an integer 
If you know your variable will always be an integer
– you can use the declare built‐in to declare it to be an 
integer 
integer
• Shell command: declare -i name
– This form of declare defines the variable name as an 
integer value

49
Conditional Expressions

50
Arithmetic Tests
• Like
Like arithmetic operations, arithmetic tests are enclosed in 
arithmetic operations, arithmetic tests are enclosed in
double parentheses. 
• The types of comparisons you can do include those 
yp p y
<= >= < > Less than or equal to, greater than or equal to, less than, greater than
comparisons.
== != Equal, not equal.
! Logical NOT.
&& Logical AND.
|| Logical OR.

• This all makes it simple to do a bit of math in a Bash script.


• Example
• We'll count up to 20 and test to see what numbers divide 51
into 20 evenly
$ cat divisors.sh
#!/bin/bash
#
declare -i testval=20
declare -i count=2 # start at 2,, 1 always
y works
while (( $count <= $testval )); do
(( result = $testval % $count ))
if (( $result == 0 )); then # evenly divisible
echo " $testval is evenly divisible by $count"
fi
(( count++ ))
done
$ bash divisors.sh
20 is evenly divisible by 2
20 is evenly divisible by 4
20 is evenly divisible by 5
20 is evenly divisible by 10
20 is evenly divisible by 20
$ _
52
Arithmetic Tests
[ ARG1 OP ARG2 ]
• "OP" is one of ‐eq, ‐ne, ‐lt, ‐le, ‐gt or ‐ge. 
– These arithmetic binary operators return true if "ARG1" is equal to, 
y p q ,
not equal to, less than, less than or equal to, greater than, or greater 
than or equal to "ARG2", respectively. 
• "ARG1"
"ARG1" and "ARG2" are integers.
d "ARG2" i t
• E.g., if [ $x –gt 10 ]; then …
• For manipulating
F i l i float
fl numbers
b use the command
h d bc
b
echo "x=5.0; x/2" | bc ‐l
bc –l # run the calculator
25/2+3.2
15.70
53
quit #quit bc
String Comparisons
• String conditional operators are
String conditional operators are
-n string True if length of string is non-zero.

-z string True if length of string is zero.

string1 == string2 g are equal.


True if strings q

string1 != string2 True if strings are not equal.

string1 < string2 string1 sorts before string2.


string1 > string2 String1 sorts after string2

x="aaa"; y="bbb"; if [ -n $x ] ; then …


x="aaa"; y="bbb"; if [ $x == $y ] ; then …

54
File-Oriented Expressions
p
-a file True if the file exists.
-d file True if the file exists and is a directory.
y
-e file True if the file exists.
-f file True if the file exists and is a regular file.
-p file True if the file exists and is a named pipe.
-r file True if the file exists and is readable.
-s
s file T if the
True th file
fil exists
i t andd has
h a size
i greater
t than
th zero.
-w file True if the file is writable.
-x file True if the file exists and is executable.
-L file True if the file exists and is a symbolic link.
file1 nt file2 True if file1 is newer than file2.
file1 ot file2 True if file1 is older than file2.
file1 ef file2 True if file1 and file2 have the same device and inode numbers.
Verify if we have the same file.
file
-O file True if the file exists and is owned by the effective user ID of the
55
user.
File-Oriented Expressions
• [[ ! EXPR ] True if EXPR is false.
! EXPR ] True if EXPR is false
• [ EXPR1 ‐a EXPR2 ] True if both EXPR1 and EXPR2 are 
true.
true
• [ EXPR1 ‐o EXPR2 ] True if either EXPR1 or EXPR2 is 
true.
• Example
if [ -f test.txt ]; then …
if [ ! -f test.sh ]; then …
if [ -f test.sh -a -f test.txt ]; then …
if [ -f test.sh ] && [ -f test.txt ]; then …
56
File-Oriented Expressions
• A simple example of a file operation might be:
p p p g
$ cat owner.sh
#!/bin/bash
#
if [ -O /etc/passwd ]; then
echo "youy are the owner of / /etc/passwd."
/p
else
echo "you are NOT the owner of
/etc/passwd "
/etc/passwd."
fi
$ bash owner.sh
you are NOT the owner of /etc/passwd.
$_

57
Control Structures

58
if .. then .. elif .. then .. else .. fi
• The if statement lets you compare two or more values and branch to a 
block of commands depending on how the values relate 
• Shell command: if
if test1; then
commands1;
[elif test2; then
commands2;]
[else commands3;]
fi
• test1 is a conditional expression, which, 
p , ,
– if true, causes the commands specified by commands1 to be executed. 
– If test1 tests false, then if an "elif" structure is present, the next test, test2, is 
evaluated ("else if"). 
( )
– If test2 evaluates to true, then the commands in commands2 are executed. 
– The "else" construct is used when you always want to run commands after a 
test evaluated as false.
– We can omit the “;” after the tests when “then” is on a new line.
59
if .. then .. elif .. then .. else .. fi
• As an example, let's assume we have a special case for a couple of our NFL teams 
when printing information about them. We might determine which file of 
information to print like this:
# $$index has been set to some arbitrary y team in the list
#
if [ "${teamname[$index]}" == "Minnesota Vikings" ]; then
cat "vikings.txt"
vikings.txt # print "special"
special info
elif [ "${teamname[$index]}" == "Chicago Bears" ]; then
cat "bears.txt" # ditto
else
cat "nfl.txt" # for everyone else, print the
standard
fi

60
if .. then .. elif .. then .. else .. fi
File Tests
• File
File test
test expressions test whether a file fits some 
expressions test whether a file fits some
particular criteria. The general syntax for a file test is
test option file
• or
[ option file ]

if test -f test.sh && test -f test.txt ; then …

61
if .. then .. elif .. then .. else .. fi
Testing that a file exists
• Consider the following if statement:
$ if [ -d /home/r/bin ] ; then PATH="$PATH:/home/r/bin" ; fi
• Here you are testing whether the directory /home/r/bin exists. If it does, 
append it to the variable PATH.
append it to the variable PATH. 
• Say you want to execute commands stored in the file $HOME/.bash_aliai if it 
exists. You can use the command
$ if [ -f $HOME/
$HOME/.bash_aliai
bash aliai ] ; then $HOME/.bash_aliai
$HOME/ bash aliai ; fi
• An improvement on this would be to test whether the file has any content and, 
if so, run the commands stored in it. 
• You can change the command to use the ‐
Y h th dt th s option instead of the ‐
ti i t d f th f option to 
f ti t
achieve this result:
if [ -s $HOME/.bash_aliai ] ; then $HOME/.bash_aliai ; fi
• Now the commands stored in the file $HOME/.bash_aliai execute if that file 
exists and has some content.

62
anny ~> cat msgcheck.sh Checking files
#!/bin/bash
echo "This scripts checks the existence of the
messages file."
echo "Checking..."
if [ -f /var/log/messages ]
then
th
echo "/var/log/messages exists."
fi
echo
echo "...done."
anny ~> ./msgcheck.sh
This scripts checks the existence of the messages file.
Checking...
/var/log/messages exists.
...done.

63
if .. then .. elif .. then .. else .. fi
Testing that a file exists
#!/bin/bash
# This script gives information about a file.
FILENAME="$1“
echo "Properties for $FILENAME:“
if [ -f $FILENAME ]; then
echo "Size
Size is $(ls -l $FILENAME | awk '{ print $5 }')“
}')
else echo "File does not exist.“
fi

64
if .. then .. elif .. then .. else .. fi
Testing exit status
if ! grep $USER /etc/passwd ; then
echo "your user account is not managed
locally";
y
fi
• The same result can be obtained as follows:
The same result can be obtained as follows:
grep $USER /etc/passwd
if [ $? -ne 0 ] ; then
echo "not a local account" ;
fi

65
if .. then .. elif .. then .. else .. fi
Numeric comparisons
#!/bin/bash
num=`wc -l count.sh | awk '{print $1}'`
echo $num
#if [ "$num" -gt "150" ]
if (( $num >= 150 ))
then
echo "you've
you ve worked hard enough for today
today."
else echo "you didn't work hard enough today"
fi
echo "end"

66
if .. then .. elif .. then .. else .. fi
Numeric comparisons
• This
This script is executed by cron every Sunday. If the week 
script is executed by cron every Sunday. If the week
number is even, it reminds you to put out the garbage cans:

#!/bin/bash
# Calculate the week number using the date command:
WEEKOFFSET $[ $(d
WEEKOFFSET=$[ $(date
t +"%V") % 2 ]
# Test if we have a remainder. If not, this is an
even week so send a message.
g
# Else, do nothing.
if [ $WEEKOFFSET -eq "0" ]; then
echo "Sunday evening, put out the garbage cans.“
else echo "It is not the time“
fi
67
if .. then .. elif .. then .. else .. fi
String comparisons
#!/bin/bash
if [ "$(whoami)" != 'root' ]; then
echo "You have no permission to run $0 as non-root
user.“

exit 1;
fi
• Regular expressions may also be used in 
comparisons use double [ ] i e [[ ]]:
comparisons, use double [ ], i.e. [[ ]]:
anny > gender="female"
annyy > if [[ "$gender"
$g == f* ]]
More input> then echo "Pleasure to meet you,
Madame."; fi
Pleasure
l to meet you, Madame.
d
anny > 68
if .. then .. elif .. then .. else .. fi
Checking command line arguments
#!/bin/bash
# This script prints a message about your weight if
you give it your
# weight
i ht i
in kil
kilos and
d h
height
i ht iin centimeters.
ti t
if [ ! $# == 2 ]; then
echo "Usage:
Usage: $0 weight
weight_in_kilos
in kilos length
length_in_cm
in cm“
exit
fi
weight="$1" ; height="$2"
idealweight=$[$height - 110]
if [ $$weight
i ht -le
l $id
$idealweight
l i ht ] ; ththen
echo "You should eat a bit more fat.“
else
echo "You should eat a bit more fruit.“ 69
fi
if .. then .. elif .. then .. else .. fi
Nested if statements
#!/bin/bash
# This script will test if we're in a leap year or not.
year=`date +%Y`
if [ $[$year % 400] -eq "0" ]; then
echo "This is a leap year. February has 29 days.“
elif [ $[$year % 4] -eq 0 ]; then
if [ $[$year % 100] -ne 0 ]; then
echo "This is a leapp yyear, Februaryy has 29 days."
y
else
echo "This is not a leap year. February has 28
d
days."
"
fi
else echo "This
This is not a leap year. February has 28
days.“
70
fi
if .. then .. elif .. then .. else .. fi
Boolean Operations
#!/bin/bash
# This script will test if we're in a leap
year or not.
y
year=`date +%Y`
if (( ("$year"
( $y % 400)
) == "0" )) || ((
("$year" % 4 == "0") && ("$year" % 100 !=
"0") )); then
echo "This is a leap year. Don't forget to
charge the extra day.“
else echo "This is not a leap year. February
has 28 days.“
fi
71
#!/bin/bash
penguin.sh
# This script lets you present different menus to Tux
Tux. He
will only be happy when given a fish. We've also added a
dolphin and (presumably) a camel.
if [ "$menu" == "fish" ]; then
if [ "$animal" == "penguin" ]; then
echo "Hmmmmmm fish... Tux happy!"
elif [ "$animal" == "dolphin" ]; then
echo "Pweetpeettreetppeterdepweet!"
else echo "*prrrrrrrt*"
prrrrrrrt ;
fi
else
if [ "$animal" == "penguin" ]; then
echo "Tux don't like that. Tux wants fish!"; exit 1;
elif [ "$animal" == "dolphin"
p ]; then
echo "Pweepwishpeeterdepweet!" ; exit 2;
else echo "Will you read this sign?!" ; exit 3;
fi
fi 72
#!/bin/bash
# This script acts upon the exit status given by
penguin.sh # export will allows using var in
penguin.sh
export menu="$1“
menu= $1 ; export animal="$2“;
animal= $2 ;
filefeed="./penguin.sh“
$feed $menu $animal
case $? in
1)echo "Guard: You'd better give'm a fish, less they
get violent
violent...";;
";;
2)echo "Guard: It's because of people like you that
they are leaving earth all the time...";;
3)echo "Guard: Buy the food that the Zoo provides
for the animals, you ***, howdo you think we
survive? ;;
survive?";;
*)echo "Guard: Don't forget the guide!";;
esac

73
case .. in .. esac
• The
The case statement lets you specify multiple actions to be 
case statement lets you specify multiple actions to be
taken when the value of a variable matches one or more 
values 
• Shell command: case
case word in
pattern { | pattern }* ) commands ;;
...
esac
– Execute the commands specified by commands when the value of 
word matches the pattern specified by pattern. 
– The ")" indicates the end of the list of patterns to match. 
– The ";;" is required to indicate the end of the commands to be 
executed
executed.
74
case .. in .. esac
• For example, a section of Bash shell script to print out the home location 
of the NFL teams listed in our earlier example might look like this:
case ${teamname[$index]} in
"Dallas Cowboys") y ) echo "Dallas, , TX" ;;
"Denver Broncos") echo "Denver, CO" ;;
"New York Giants"|"New York Jets") echo "New York,
NY";;
. . .
*) echo "Unknown location" ;;
esac
• Note the special use of the pattern "*" as the last pattern. 
• If you go through all the patterns and have never matched anything, this is 
useful to catch this situation. 
• It is permissible to not match any patterns, in which case, none of the 
I i i ibl h i hi h f h
commands will be executed. 

75
#!/bin/bash
# This script
p does a very y simple
p test for checking
g
disk space.
space=`df -h | awk '{print $5}' | grep % | grep -v
Use | sort -nn | tail -11 | cut -dd "%" –f
f 1`
1
case $space in
[1-6]*)Message="All
[ ] ) g is q
quiet.";;;;
[7-8]*)Message="Start thinking about cleaning out
some stuff. There's a partition that is $space %
full ";;
full.";;
9[1-8])Message="Better hurry with that new disk...
One partition is $space % full.";;
99)Message="I'm drowning here! There's a partition
at $space %!";;
*)Message="I seem to be running with an nonexistent
amount of disk space...";;
esac
echo $Message
76
#!/bin/bash
echo menu test program
stop=0 # reset loop termination flag.
while test $stop -eq
eq 0 # loop until done.
do
cat << ENDOFMENU # display menu.
1 : print the date.
2, 3: print the current working directory
directory.
4 : exit
ENDOFMENU
echo
echo -nn 'your
your choice? ' # prompt
prompt.
read reply # read response.
echo
case $reply in # process response.
1 )
"1")
date # display date.
;;
"2"|"3")
pwd # display working directory
directory.
;;
"4")
stop=1 # set loop termination flag.
;;
*) # default.
echo illegal choice # error.
;;
esac
echo 77
done
$ bash menu.sh
menu test program
1 : print the date.
2 3
2, 3: print
i t th
the current
t working
ki di
directory.
t
4 : exit
your choice? 1
Thu May 5 07:09:13 CST 2005
1 : print the date.
2, 3: print the current working directory.
4 : exit
your choice? 2
/home/glass
1 : print the date.
2, 3: print the current working directory.
4 : exit
your choice? 5
illegal choice
1 : print the date.
2, 3: print the current working directory.
4 : exit
your choice? 4
78
$ _
for .. do .. done
• The
The for
for construct is best used when you have a known set of 
construct is best used when you have a known set of
items you wish to iterate over.
• You might use a comparison to jump out of such a loop, or 
g p j p p,
you might simply wish to process each item in the list 
sequentially.
• Shell command: for
for name in word { word }*
do
commands
done
Perform commands for each word in list with $name 
containing the value of the current word. Another form:
for NAME [in LIST ]; do COMMANDS; done 79
for .. do .. done
• One example of this use might be 
p g
– if you needed to sort the contents of all text files in a directory:
$ ls
abc.txt def.txt ghi.txt
$ for file in *.txt
do
sort $file > $file.sorted
done
$ lls
abc.txt def.txt ghi.txt
abc.txt.sorted def.txt.sorted
ghi.txt.sorted
$ _

80
for .. do .. done
Using command substitution for specifying
LIST items
• Using command substitution for specifying LIST 
Using command substitution for specifying LIST
items
ls *
*.sh
sh > list
for i in `cat list`; do
cp "$i" "$i"
"$i".bak
bak ; done
• This one lists the files in /sbin that are just plain text 
fil
files, and possibly scripts:
d ibl i t
for i in `ls /sbin`; do
file /sbin/$i
/ /$ | grep ASCII; done

81
for .. do .. done
Manipulating a Set of Files
• Say that you need to copy a bunch of files
y y py from one directory 
y
to another and change the permissions on the copy. 
• You could do this by copying each file and changing the 
permissions manually.
i i ll
• A better solution would be to determine the commands you 
need to execute in order to copy a file and change its
need to execute in order to copy a file and change its 
permissions and then have the computer do this for every file 
you were interested in. 
• In fact this is one of the most common uses of the for loop –
– iterating over a set of file names and performing some operations 
on those files
on those files.

82
for .. do .. done
Manipulating a Set of Files
• The procedure to do this follows:
p
1. Create a for loop with a variable named file or FILE. Other favored 
names include i, j, and k. Usually the name of the variable is singular.
p q y p
2. Create a list of files to manipulate. This is frequently accomplished 
using the filename substitution 
3. Manipulate the files in the body of the loop.
• An
An example of this is the following for loop:
example of this is the following for loop:
for FILE in $HOME/.bash*
do
cp $FILE ${HOME}/public_html
chmod a+r ${HOME}/public_html/${FILE}
#cd ${HOME}/public
${HOME}/public_html html
#chmod a+r .bash*
done
do e
83
for .. do .. done
Manipulating a Set of Files
• In this loop I use filename substitution to obtain a list 
of files in my home directory that start with .bash*. 
• In the body of the loop, 
– I copy each of these files to my public_html directory, 
– and then I make them readable by everyone. 
– This way people stopping by my Web page can see the 
Thi l t i b W b th
scripts.
• Notice
Notice that you are using the name FILE for the 
that you are using the name FILE for the
variable. 
– This is because each time you are dealing with a single file 
f
from a list of files. 
li t f fil
– The rationale behind making the for loop's variable 
singular, such as FILE instead of FILES, is that you are 
g , , y
dealing with only one item from a set of items each time 
the loop executes. 84
Loop Control
The break command
• The break command accepts as an argument an integer, greater or equal 
to 1, indicating the number of levels to break out of. 
d h b fl l b k f
• This feature is useful when nested loops are being used. Consider the 
following nested for loops:
for i i
f in 1 2 3 4 5
do
mkdir -p /mnt/backup/docs/ch0${i}
if [ $? -eq 0 ] ; th
then
for j in doc c h m pl sh
do
cp $HOME/docs/ch0${i}/*.${j}
$HOME/docs/ch0${i}/* ${j} /mnt/backup/docs/ch0${i}
if [ $? -ne 0 ] ; then break 2 ; fi
done
else
echo "Could not make backup directory."
fi
done

85
Loop Control
The break command
• In
In this loop, I
this loop, I'm
m making a backup of several 
making a backup of several
important files from my home directory to the 
backup directory.
p y
• The outer loop takes care of creating the backup 
directory, whereas the inner loop copies the 
y p p
important files based on the extension.
p y
• In the inner loop, you have a break command with 
the argument 2. 
– This indicates that if an error occurs while copying you 
should break out of both loops, and not just the inner 
loop.

86
Loop Control
The continue command
• The continue command is similar to the break command, except that it 
causes the current iteration of the loop to exit, rather than the entire 
h f h l h h h
loop. 
• This command is useful when an error has occurred but you want to try to 
execute the next iteration of the loop
execute the next iteration of the loop.
• As an example, the following loop doesn't exit if one of the input files is 
bad:
for FILE in $FILES ;
do
if [ ! -f "$FILE" ] ; then
echo "ERROR: $FILE is not a file
file."
"
continue
fi
# process the file
done
• If one of the filenames in $FILES is not a file, this loop skips it, rather than 
exiting
exiting.
87
while/until .. do .. done
• Shell commands: while/until
/
while test
do
commands
done
u t
until test
do
commands
done
• In a while statement, perform commands as long as the 
expression test evaluates to true. 
• In an until
I il statement, perform commands as long as the 
f d l h
expression test evaluates to false (i.e., until test is true).

88
while/until .. do .. done
• These constructs are useful when you don't know exactly when the status 
of the test condition will be changed. 
• Here's an example of a script that uses an until control structure:
$ cat until
until.sh sh ...list
list the script
script.
x=1
until [ $x -gt 3 ]
do
echo x = $x
(( x = $x + 1 )) # x=$[$x+1]
done
$ bash until.sh ...execute the script.
x = 1
x = 2
x = 3
$ _

89
• Here's an example of a script that uses a while control structure to 
generate a small multiplication table:
$ cat multi.sh ...list the script.
if [ $# -lt 1 ]; then
echo "Usage:g multi number"
exit
fi
x=1 # set outer loop value
while [ $x -le $1 ] # outer loop
do
y=1 # set inner loop value
while
hil [ $ $y -lel $1 ]
do # generate one table entry
(( entry = $x * $y ))
echo
h -e -n "$entry\t"
"$ t \t"
(( y = $y + 1 )) # update inner loop count
done
echo # blank line
(( x = $x + 1 )) # update outer loop count
done

use the –e in echo to be able to use options90


like \t (tabulation), -n removes the new line.
while/until .. do .. done
$ bash multi.sh 7 ...execute the script.
p
1 2 3 4 5 6 7
2 4 6 8 10 12 14
3 6 9 12 15 18 21
4 8 12 16 20 24 28
5 10 15 20 25 30 35
6 12 18 24 30 36 42
7 14 21 28 35 42 49

91
Nesting while Loops
x=0
#while [ $x -lt 10 ]
while ((x < 10 ))
do
y=$x
# while [ $y -ge 0 ]
while (( y >= 0 ))
do
Output
echo -n "$y " 0
((y=y 1))
((y=y-1)) 10
done 210
echo 3210
((
((x=x+1))
1)) 43210
done 543210
6543210
76543210
876543210 92
9876543210
Example
#!/bin/bash
# This generates a file every 5
minutes
while true; do
touch pic-`date +%s`.txt
p 300
sleep
done

93
#!/bin/bash
# This script provides
wisdom
FORTUNE=/usr/games/fortune
while true; do
echo "On which topic do
you want advice?"
advice?
cat << topics echo
politics
startrek
echo -n
n "Make
Make your choice: "
kernelnewbies read topic
sports echo
bofh-excuses
echo "Free advice on the
magic
topic of $topic: "
love
literature echo
drugs $FORTUNE $topic
education echo
topics done 94
#!/bin/bash
# Calculate the average of a series of numbers.
SCORE="0" ; AVERAGE="0"
SUM="0" ; NUM="0"
while
hil ttrue; d
do
echo -n "Enter your score [0-100%] ('q' for quit): "
read SCORE;
if (("$SCORE" < "0")) || (("$SCORE" > "100")); then
echo "Be serious. Common, try again: "
elif [ "$SCORE"
$SCORE == "q"
q ]; then
echo "Average rating: $AVERAGE%."
break
else
SUM=$[$SUM + $SCORE]
NUM=$[$NUM
U $[$ U + 1]]
AVERAGE=$[$SUM / $NUM]
fi
done
echo "Exiting." 95
Validating User Input
• Say that you need to write a script that needs to ask the user 
y y p
for the name of a directory. You can use the
• following steps to get information from the users:
1. Ask
1 Ask the user a question.
the user a question
2. Read the user's response.
3. Check to see whether the user responded with the name of a 
directory.
directory
• What should you do when the user gives you a response that 
is not a directory?
• The simplest choice would be to do nothing, but this is not 
very user friendly. 
• Your script can be much more user friendly
Your script can be much more user friendly by informing the 
by informing the
user of the error and asking for the name of a directory again.

96
Validating User Input
• The
The while
while loop is perfect for doing this. 
loop is perfect for doing this.
• In fact, one of the most common uses for the while loop is to 
check whether user input has been gathered correctly. 
p g y
• Usually a strategy similar to the following is employed:
1. Set a variable's value to null.
2. Start a while loop that exits when the variable's value is not null.
3. In the while loop, ask the user a question and read in the users 
response.
response
4. Validate the response.
5. If the response is invalid
p the variable's value is set to null. This enables 
the while loop to repeat.
6. If the response is valid, the variable's value is not changed. It continues 
to hold the user's response Because the variable's value is not null
to hold the user's response. Because the variable's value is not null, 
the while loop exits. 97
Validating User Input
• In the following example, use the commands
– echo to display a string
– read to read in the user's response
• A while loop follows that solves your problem:
A while loop follows that solves your problem:
RESPONSE=
while [ -z "$RESPONSE" ] ; # true if length of var is 0
do
echo "Enter the name of a directory where your files
are located:"
read RESPONSE
if [ ! -d "$RESPONSE" ] ; then
echo "ERROR: Please enter a directory pathname."
RESPONSE=
fi
done
do e
98
Functions

Bash allows you to define functions that 
y
may be invoked as shell commands. 

99
Functions definition
• Parameters passed to functions are accessible via the standard positional 
parameter mechanism. 
h
• Functions must be defined before they are used. 
• There are two ways to define a function
• function name
{
list of commands
}
or the keyword function may be omitted:
name ()
{
list of commands.
}
• To invoke a function, supply its name followed by the appropriate 
parameters. 
• For obvious reasons, the shell does not check the number or type of the 
parameters.

100
• Here's an example of a script that defines and uses a function that takes 
no parameters:

$ cat func1.sh ...list the script.


message () # no-parameter function.
{
echo hi
echo there
}
i=1
while (( i <= 3 ))
do
message # call the function.
let i=i+1 # increment loop count.
done
$ sh func1.sh ...execute the script.
hi
there
hi
there
hi
there 101
$ _
Using Parameters
• As I mentioned previously, parameters are accessible via the standard positional 
mechanism. Here's an example of a script that passes parameters to a function:
h i H ' l f i t th t t t f ti

$ cat func2.sh ...list the script.


f ()
{
echo parameter 1 = $1 # display first parameter.
echo parameter list = $* # display entire list.
}
# main program.
f 1 # call with 1 parameter.
f cat dog goat # call with 3 parameters.
$ sh func2.sh ...execute the script.
parameter 1 = 1
parameter
t li
listt = 1
parameter 1 = cat
parameter list = cat dog goat

102
Returning from a Function
• The
The return command returns the flow of control back to the 
return command returns the flow of control back to the
caller, and has the following syntax 
return [ value ]
– When return is used without an argument, the function call returns 
immediately with the exit code of the last command that was 
executed in the function;
executed in the function; 
– otherwise, it returns with its exit code set to value. 
– If a return command is executed from the main script, it's equivalent 
p, q
to an exit command. 
– The exit code is accessible from the caller via the $? variable. 
• Here's an example function that multiplies its arguments and 
returns the result:

103
Returning from a Function
$ cat func3.sh ...list the script.
p
f () # two-parameter function.
{
(( returnValue = $1 * $2 ))
return $returnValue
}
# main program.
f 3 4 # call function.
result=$? # save exit code
code.
echo return value from function was $result
$ sh func3.sh ...execute the script.
return value
l f
from f
function
i was 12
$ _

104
Access to Functions
• Functions can be exported to subshells in Bash. 
Functions can be exported to subshells in Bash.
• This is accomplished with the export built‐in 
• Shell command: export
Shell command: export -f functionname
– The export built‐in command used with the ‐f option 
exports a function to a subshell the same way exported
exports a function to a subshell the same way exported 
shell variable values are exported to subshells.

• Shell command: local name[=value]
– The local built‐in command defines a variable to be local 
only to the current function. A variable name can be listed 
or a value can be assigned in the same statement.

105
Recursion: Recursive Factorial,
Using Exit Code
• With careful thought, it's perfectly possible to write recursive functions. 
factorial () # one-parameter function
{
if (( $1 <= 1 ))
then
return 1 # return result in exit
code.
else
typeset
yp tmp
p # declare two local variables.
typeset result
(( tmp = $1 - 1 ))
factorial $tmp # call recursively.
(( result = $
$? * $$1 ))
return $result # return result in exit code.
fi
}
# main program.
factorial 5 # call function
echo factorial 5 = $? # display exit code.

106
Recursion: Recursive Factorial,
Using Standard Output
factorial() # one-parameter function
{
if (( $1 <= 1 ))
then
echo 1 # echo result to standard output
output.
else
typeset tmp # declare two local variables.
typeset result
(( tmp = $1 - 1 ))
(( result = `factorial $tmp ` * $1 ))
echo $result # echo result to standard output.
fi
}
#
echo factorial $1 = ` factorial $1 ` # display
result.
lt

107
Sharing Functions
• To
To share the source code of a function between 
share the source code of a function between
several scripts, place it in a separate file and then 
read it using the "." or source
g built‐in commands at 
the start of the scripts that use the function 
• Shell Command: source fileName
. fileName
– source (or ".") causes a shell to execute every command in 
the script called fileName without invoking a subshell. 
– It is perfectly legal for fileName to contain further source 
commands. 
commands
– If an error occurs during the execution of fileName, control 
g
is returned to the original shell.
108
Sharing Functions
• In
In the following example, assume that the source code 
the following example assume that the source code
of one of the previous factorial scripts was saved in a 
file called "fact
file called  fact.sh
sh"::
$ cat script.sh ...list the script.
. fact.sh # read function source code.
echo factorial 5 = 'factorial 5' # call the function.
$ sh script.sh ...execute the script.
factorial 5 = 120
$ _

109
Menus: select
• The select command allows you to create simple menus, and 
y p ,
has the syntax  
select name [ in {word }+ ]
do
list
done
• The select command displays a numbered list of the words 
specified by the in clause to the standard error channel
specified by the in clause to the standard error channel, 
displays the prompt stored in the special variable PS3, and 
then waits for a line of user input. 
• When the user enters a line, it's stored in the predefined 
variable REPLY, and then one of three things occurs:

110
Menus: select
1. If
If the user entered one of the listed numbers, name is set to 
the user entered one of the listed numbers, name is set to
that number, the commands in list are executed, and then 
the user is prompted for another choice.
2. If the user entered a blank line, the selection is displayed 
again.
3. If the user entered an illegal choice, name is set to null, the 
commands in the list are executed, and then the user is 
prompted for another choice.
t df th h i
• The next example is a recoding of the menu‐selection 
example from earlier in the chapter
example from earlier in the chapter. 
• It replaces the while loop and termination logic with a 
simpler select command
simpler select command. 
111
$ cat newmenu.sh ...list the script.
echo menu test program
select reply in "date" "pwd" "pwd" "exit"
do
case $
$reply
p y in
"date")
date
;; $ sh newmenu
newmenu.sh
sh ...execute
execute the
"pwd") script.
pwd menu test program
;;
1) date
"exit")
break 2) pwd
;; 3) pwd
*) 4) exit
it
echo illegal choice #? 1
;; Fri May 6 21:49:33 CST 2005
esac #? 5
done illegal choice
#? 4
$ _
112
Exercises

113
Exercise 1: junk new utility
• Write
Write a utility called junk
a utility called junk that satisfies the following 
that satisfies the following
specification:
• Utility: junk
Utility: junk [ [-l]l] [ [-p]
p] { fileName }*
– junk is a replacement for the rm utility. 
– Rather than removing files, it moves them into the 
R h h i fil i h i h
subdirectory ".junk" in your home directory. 
– If ".junk" doesn't exist, it is automatically created. 
If " junk" doesn't exist it is automatically created
– The ‐l option lists the current contents of the ".junk" 
directory and
directory, and 
– the ‐p option purges ".junk".

114
Exercise 1 (cont.):
junk new utility
Here's an example of junk
p j at work:
$ ls -lG reader.c ...list existing file.
-rw-r--r-- 1 glass 2580 May 4 19:17 reader.c
$ j
junk reader.c ...junk
j it!
$ ls -lG reader.c ...confirm that it was moved.
reader.c not found
$ junk badguy.c ...junk another file.
$ junk -l ...list contents of "junk" directory.
-rw-r--r-- 1 glass 57 May 4 19:17 badguy.c
-rw-r--r-- 1 glass 2580 May 4 19:17 reader.c
$ junk -p ...purge junk.
$ junk -l ...list junk.
$ _

115
Exercise 2: Process management
• Create
Create a process management utility that allows you 
a process management utility that allows you
to kill processes based on their CPU usage. 
• This kind of utility would be especially useful to 
This kind of utility would be especially useful to
system administrators 

Extra question:
• Write a shell script that monitor your system by 
testing the cpu usage each 5 seconds, and report to 
g p g , p
a logfile the date and name of processes that 
exceeds a given percentage that should be precise 
g p g p
from the command line. 116
Exercise 3: Move and rename files
• Write
Write a Bash script called mv (which replaces the GNU utility 
a Bash script called mv (which replaces the GNU utility
mv) that tries to rename the specified file (using the GNU 
utility mv), but if the destination file exists, instead creates an 
index number to append to the destination file, a sort of 
version number. So if I type:
• $ mv a.txt b.txt
• but b.txt already exists, mv will move the file to b.txt.1. Note 
th t if b t t 1 already exists, you must rename the file to 
that if b.txt.1 l d it t th fil t
b.txt.2, and so on, until you can successfully rename the file 
to a name that does not already exist
to a name that does not already exist. 

117
Exercise 4: Tracing
What is the output on the screen of the following code?
x=0
while [ $x -lt 10 ]
do
y=$x
while [ $y -ge
ge 0 ]
do
echo -n "$y "
((y=y-1))
done
echo
((x=x+1))
done
118
Exercise 5: Files Backup
• We want to backup all files in the directory /home/tony/OS
We want to backup all files in the directory /home/tony/OS
with the extension .ppt, .doc and .xls. For that, do the 
following: 
1. Firstly, test if the file update.txt exists in the home directory 
and contains the date of the current day. If the condition is 
true proceed in the second step, else print out a goodbye 
message and exit. Note that the test option –z string is true if 
string has zero length.
has zero length
2. Secondly, compress all the files with the extension .ppt, .doc
and .xls and then move the compressed files to the
and .xls, and then move the compressed files to the 
directory ~/backup. You need to test if the backup directory 
exists, if not you have to create it. 
119
Exercise 6: Process monitoring
• We want to monitor the running processes that are 
gp
consuming more than 90% of the system memory. For that,
1. Write a shell program that output to the screen the process 
ID of the first running process that is taking more than 90% 
f h fi i h i ki h %
of the system memory. You can use awk programming. 
• An example of the ps u
An example of the ps u command gives:
command gives:
USER       PID   %CPU %MEM    VSZ   RSS  TTY      STAT START   TIME COMMAND
ralph      4000  0.0      0.6              5820    3128  pts/0     Ss       11:42        0:00    bash
ralph
ralph      7952 0 0 0 2
7952  0.0      0.2              2768    1028  pts/0     R+      14:51       0:00     ps u
2768 1028 pts/0 R+ 14:51 0:00 ps u

2. Now, we want to force all the processes consuming more 


than 90% of the memory to be killed. What is the 
corresponding code?  You can use arrays.

120
• Exercise 7
– Write
Write a shell program that displays the name of files in a 
a shell program that displays the name of files in a
given directory and the line numbers of each file.
– The program should go through all sub directories of the 
chosen directory.
h di t
– The program takes as an argument the name of the 
y
directory.

• Exercise 8
– Write a shell program that accepts three arguments and displays them 
in inverse order 

• Exercise 9
– Write a shell program that displays the system date, the list of 
connected users and the processes list
connected users, and the processes list

• Exercise 10
Exercise 10
– Write a shell program that calculates the factorial of a number using 121
functions.
• Exercise 11
– Write a shell program that displays the file names of a given directory 
p g p y g y
and that have the following characteristics:
• They are “read only” files
• Contain some specific texts (to be entered as argument)
p ( g )
– Input the directory name and the string of characters that are to be 
searched.

• Exercise 12
– Write a shell program that allows to verify and to display if a given 
user is the owner of a given directory.
i th f i di t
– Input the name of the directory and the user name as arguments

• Exercise 13
– Write a shell program that can verify the existence of a given file in a 
given hierarchy of directories The program should display if the file is
given hierarchy of directories. The program should display if the file is 
found inside the current directory or sub directory.
– Input the name of the directory and file as arguments

122
• Exercise 14
– Write
Write a script that takes exactly one argument, a directory name. 
a script that takes exactly one argument a directory name
– If the number of arguments is more or less than one, print a usage 
message. 
– If the argument is not a directory, print another message. 
– For the given directory, print the five biggest files and the five files 
that were most recently modified.

• Exercise 15
– Display a list of all the users on your system who are logged in.
– From the /etc/group directory, display all lines starting with the string 
"daemon"
daemon .

• Exercise 16
Exercise 16
– Make a list of files in your home directory that were changed less than 
10 hours ago, using grep.

123
• Exercise 17
– What are the current permissions for your home directory?  
– What permissions do users in your group have in your home 
directory?  
y
– What permissions do users not in your group have in your directory?

• Exercise 18
– Move to different directories on the Unix file system and try using the 
y y g
file command to see what different types of files are on the system.  
– How about the /bin or /dev directories?  
– What types of files can you find?
Wh t t f fil fi d?
– Use the df command with the ‐k option to find out how full the "/" 
filesystem is.  What is their capacity, and their space available?
– Use the find command to search for files named lpsched file in the 
/usr directory system.

124

You might also like