Unix Shell Programming-Tutorial
Unix Shell Programming-Tutorial
A shell is an environment in which we can run our commands, programs, and shell scripts. There are
different flavors of shells, just as there are different flavors of operating systems. Each flavor of shell has its
own set of recognized commands and functions.
Shell Prompt:
The prompt, $, which is called command prompt, is issued by the shell. While the prompt is displayed, you
can type a command.
The shell reads your input after you press Enter. It determines the command you want executed by looking
at the first word of your input. A word is an unbroken set of characters. Spaces and tabs separate words.
Following is a simple example of date command which displays current date and time:
Shell Types:
In UNIX there are two major types of shells:
1. The Bourne shell. If you are using a Bourne-type shell, the default prompt is the $ character.
2. The C shell. If you are using a C-type shell, the default prompt is the % character.
There are again various subcategories for Bourne Shell which are listed as follows:
C shell ( csh)
The Bourne shell is usually installed as /bin/sh on most versions of UNIX. For this reason, it is the shell of
choice for writing scripts to use on several different versions of UNIX.
Shell Scripts:
A shell script is a list of commands, which are listed in the order of execution. A good shell script will have
comments, preceded by a pound sign, #, describing the steps.
There are conditional tests, such as value A is greater than value B, loops allowing us to go through massive
amounts of data, files to read and store data, and variables to read and store data, and the script may
include functions.
Shell scripts and functions are both interpreted. This means they are not compiled.
Example Script:
To create a [Link] script. All the scripts would have .sh extension. Before adding anything else to the script,
you need to alert the system that a shell script is being started. This is done using the shebang construct.
For example:
#!/bin/sh
This tells the system that the commands that follow are to be executed by the Bourne shell. It's called a
shebang because the # symbol is called a hash, and the ! symbol is called a bang.
To create a script containing these commands, put the shebang line first and then add the commands:
#!/bin/bash
pwd
ls
Shell Comments:
#!/bin/bash
# Author : Ramya.U.M
# Script follows here:
pwd
ls
Now you save the above content and make this script executable as follows:
/home/staff/ase/it/um_ramya
Following script use the read command which takes the input from the keyboard and assigns it as the value
of the variable PERSON and finally prints it on STDOUT.
#!/bin/sh
[um_ramya@ssh ~]$./[Link]
What is your name?
Sudhi
Hello, Sudhi
[um_ramya@ssh ~]$
A variable is nothing more than a pointer to the actual data. The shell enables you to create, assign, and
delete variables.
Variable Names:
The name of a variable can contain only letters ( a to z or A to Z), numbers ( 0 to 9) or the underscore
character ( _).
_ALI
TOKEN_A
VAR_1
VAR_2
The reason you cannot use other characters such as !,*, or - is that these characters have a special meaning
for the shell.
Defining Variables:
Variables are defined as follows::
variable_name=variable_value
For example:
NAME="Shalini"
Above example defines the variable NAME and assigns it the value " Shalini ". Variables of this type are
called scalar variables. A scalar variable can hold only one value at a time.
The shell enables you to store any value you want in a variable. For example:
VAR1=" Shalini"
VAR2=100
Accessing Values:
To access the value stored in a variable, prefix its name with the dollar sign ( $):
For example, following script would access the value of defined variable NAME and would print it on STDOUT:
#!/bin/sh
NAME="Shalini"
echo $NAME
Shalini
Read-only Variables:
The shell provides a way to mark variables as read-only by using the readonly command. After a variable is
marked read-only, its value cannot be changed.
For example, following script would give error while trying to change the value of NAME:
#!/bin/sh
NAME="Shalini"
readonly NAME
NAME="Preethi"
Unsetting Variables:
Unsetting or deleting a variable tells the shell to remove the variable from the list of variables that it tracks.
Once you unset a variable, you would not be able to access stored value in the variable.
Following is the syntax to unset a defined variable using the unset command:
unset variable_name
Above command would unset the value of a defined variable. Here is a simple example:
#!/bin/sh
NOTE: Above example would not print anything. You cannot use the unset command to unset variables that
are marked readonly.
Variable Types:
When a shell is running, three main types of variables are present:
Local Variables: A local variable is a variable that is present within the current instance of the
shell. It is not available to programs that are started by the shell. They are set at command prompt.
Environment Variables: An environment variable is a variable that is available to any child
process of the shell. Some programs need environment variables in order to function correctly.
Usually a shell script defines only those environment variables that are needed by the programs that
it runs.
Shell Variables: A shell variable is a special variable that is set by the shell and is required by the
shell in order to function correctly. Some of these variables are environment variables whereas
others are local variables.
Special Variables:
#!/bin/sh
There are special parameters that allow accessing all of the command-line arguments at once. $*
and $@ both will act the same unless they are enclosed in double quotes, "".
Both the parameter specifies all command-line arguments but the "$*" special parameter takes
the entire list as one argument with spaces between and the "$@" special parameter takes the
entire list and separates it into separate arguments.
We can write the shell script shown below to process an unknown number of command-line
arguments with either the $* or $@ special parameters:
#!/bin/sh
for TOKEN in $*
do
echo $TOKEN
done
Note: Here do...done is a kind of loop which we would cover in subsequent tutorial.
Exit Status:
Exit status is a numerical value returned by every command upon its completion. As a rule, most
commands return an exit status of 0 if they were successful, and 1 if they were unsuccessful.
Some commands return additional exit statuses for particular reasons. For example, some
commands differentiate between kinds of errors and will return various exit values depending on
the specific type of failure.
Shell supports a different type of variable called an array variable that can hold multiple values at the same
time. Arrays provide a method of grouping a set of variables. Instead of creating a new name for each
variable that is required, you can use a single array variable that stores all the other variables.
All the naming rules discussed for Shell Variables would be applicable while naming arrays.
Say that you are trying to represent the names of various students as a set of variables. Each of the
individual variables is a scalar variable as follows:
NAME01="Ahan"
NAME02="Tuhinaa"
NAME03="Diya"
NAME04="Aradhana"
NAME05="Adhiraa"
We can use a single array to store all the above mentioned names. Following is the simplest method of
creating an array variable is to assign a value to one of its indices. This is expressed as follows:
array_name[index]=value
Here array_name is the name of the array, index is the index of the item in the array that you want to set,
and value is the value you want to set for that item.
NAME[0]="Ahan"
NAME[1]="Tuhinaa"
NAME[2]="Diya"
NAME[3]="Aradhana"
NAME[4]="Adhiraa"
If you are using ksh shell then, here is the syntax of array initialization:
If you are using bash shell then, here is the syntax of array initialization:
${array_name[index]}
Here array_name is the name of the array, and index is the index of the value to be accessed. Following is
the simplest example:
#!/bin/sh
NAME[0]="Ahan"
NAME[1]="Tuhinaa"
NAME[2]="Diya"
NAME[3]="Aradhana"
NAME[4]="Adhiraa"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"
[um_ramya@ssh ~]$./[Link]
First Index: Ahan
Second Index: Tuhinaa
You can access all the items in an array in one of the following ways:
${array_name[*]}
${array_name[@]}
Here array_name is the name of the array you are interested in. Following is the simplest example:
#!/bin/sh
NAME[0]="Ahan"
NAME[1]="Tuhinaa"
NAME[2]="Diya"
NAME[3]="Aradhana"
NAME[4]="Adhiraa"
echo "First Method: ${NAME[*]}"
echo "Second Method: ${NAME[@]}"
[um_ramya@ssh ~]$./[Link]
First Method: Ahan Tuhinaa Diya Aradhana Adhiraa
Second Method: Ahan Tuhinaa Diya Aradhana Adhiraa
Arithmetic Operators.
Relational Operators.
Boolean Operators.
String Operators.
File Test Operators.
The Bourne shell didn't originally have any mechanism to perform simple arithmetic but it uses external
programs, either awk or the must simpler program expr.
#!/bin/sh
val=`expr 2 + 2`
echo "Total value : $val"
Total value : 4
NOTE:
There must be spaces between operators and expressions for example 2+2 is not correct, where as
it should be written as 2 + 2.
Complete expression should be enclosed between ``, called inverted commas ( symbol which is
along with ~).
Arithmetic Operators:
Following arithmetic operators are supported by Bourne Shell.
Operato
Description Example
r
+ Addition - Adds values on either side of the operator `expr $a + $b` will give 30
- Subtraction - Subtracts right hand operand from left `expr $a - $b` will give -10
hand operand
* Multiplication - Multiplies values on either side of the `expr $a * $b` will give 200
operator
/ Division - Divides left hand operand by right hand `expr $b / $a` will give 2
operand
% Modulus - Divides left hand operand by right hand `expr $b % $a` will give 0
operand and returns remainder
= Assignment - Assign right operand in left operand a=$b would assign value of b into a
== Equality - Compares two numbers, if both are same [ $a == $b ] would return false.
then returns true.
!= Not Equality - Compares two numbers, if both are [ $a != $b ] would return true.
different then returns true.
It is very important to note here that all the conditional expressions would be put inside square braces with
one spaces around them, for example [ $a == $b ] is correct where as [$a==$b] is incorrect.
Relational Operators:
Bourne Shell supports following relational operators which are specific to numeric values. These operators
would not work for string values unless their value is numeric.
For example, following operators would work to check a relation between 10 and 20 as well as in between
"10" and "20" but not in between "ten" and "twenty".
Operato
Description Example
r
-eq Checks if the value of two operands are equal or [ $a -eq $b ] is not true.
not, if yes then condition becomes true.
-ne Checks if the value of two operands are equal or [ $a -ne $b ] is true.
not, if values are not equal then condition becomes
true.
-gt Checks if the value of left operand is greater than [ $a -gt $b ] is not true.
the value of right operand, if yes then condition
becomes true.
-lt Checks if the value of left operand is less than the [ $a -lt $b ] is true.
value of right operand, if yes then condition
becomes true.
-ge Checks if the value of left operand is greater than or [ $a -ge $b ] is not true.
equal to the value of right operand, if yes then
condition becomes true.
-le Checks if the value of left operand is less than or [ $a -le $b ] is true.
equal to the value of right operand, if yes then
condition becomes true.
It is very important to note here that all the conditional expressions would be put inside square braces with
one spaces around them, for example [ $a <= $b ] is correct where as [$a <= $b] is incorrect.
Boolean Operators:
Following boolean operators are supported by Bourne Shell.
Operato
Description Example
r
-o This is logical OR. If one of the operands is true then [ $a -lt 20 -o $b -gt 100 ] is true.
condition would be true.
-a This is logical AND. If both the operands are true [ $a -lt 20 -a $b -gt 100 ] is false.
then condition would be true otherwise it would be
false.
String Operators:
Following string operators are supported by Bourne Shell.
Operato
Description Example
r
str Check if str is not the empty string. If it is empty [ $a ] is not false.
then it returns false.
Assume a variable file holds an existing file name "test" whose size is 100 bytes and has read, write and
execute permission on:
Show Examples
Operato
Description Example
r
-b file Checks if file is a block special file if yes then [ -b $file ] is false.
condition becomes true.
-c file Checks if file is a character special file if yes then [ -b $file ] is false.
condition becomes true.
-d file Check if file is a directory if yes then condition [ -d $file ] is not true.
becomes true.
-g file Checks if file has its set group ID (SGID) bit set if yes [ -g $file ] is false.
then condition becomes true.
-k file Checks if file has its sticky bit set if yes then [ -k $file ] is false.
condition becomes true.
-p file Checks if file is a named pipe if yes then condition [ -p $file ] is false.
becomes true.
-t file Checks if file descriptor is open and associated with [ -t $file ] is false.
a terminal if yes then condition becomes true.
-u file Checks if file has its set user id (SUID) bit set if yes [ -u $file ] is false.
then condition becomes true.
-s file Check if file has size greater than 0 if yes then [ -s $file ] is true.
condition becomes true.
-e file Check if file exists. Is true even if file is a directory [ -e $file ] is true.
but exists.
Unix Shell supports conditional statements which are used to perform different actions based on different
conditions.
The if...fi statement is the fundamental control statement that allows Shell to make decisions and execute
statements conditionally.
Syntax:
if [ expression ]
then
Statement(s) to be executed if expression is true
fi
Here Shell expression is evaluated. If the resulting value is true, given statement(s) are executed.
If expression is false then no statement would be not executed. Most of the times you will use comparison
operators while making decisions.
Give you attention on the spaces between braces and expression. This space is mandatory otherwise you
would get syntax error.
If expression is a shell command then it would be assumed true if it return 0 after its execution. If it is a
boolean expression then it would be true if it returns true.
Example:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
fi
if [ $a != $b ]
then
echo "a is not equal to b"
fi
a is not equal to b
Syntax:
if [ expression ]
then
Statement(s) to be executed if expression is true
else
Statement(s) to be executed if expression is not true
fi
Here Shell expression is evaluated. If the resulting value is true, given statement(s) are executed.
If expression is false then no statement would be not executed.
Example:
If we take above example then it can be written in better way using if...else statement as follows:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
else
echo "a is not equal to b"
fi
a is not equal to b
Syntax:
if [ expression 1 ]
then
Statement(s) to be executed if expression 1 is true
elif [ expression 2 ]
then
Statement(s) to be executed if expression 2 is true
elif [ expression 3 ]
then
Statement(s) to be executed if expression 3 is true
else
Statement(s) to be executed if no expression is true
Fi
There is nothing special about this code. It is just a series of if statements, where each if is part of
the else clause of the previous statement. Here statement(s) are executed based on the true condition, if
non of the condition is true then else block is executed.
Example:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
elif [ $a -gt $b ]
then
echo "a is greater than b"
elif [ $a -lt $b ]
then
echo "a is less than b"
else
echo "None of the condition met"
fi
a is less than b
Unix Shell supports case...esac statement which handles exactly this situation, and it does so more
efficiently than repeated if...elif statements.
You can use multiple if...elif statements to perform a multiway branch. However, this is not always the best
solution, especially when all of the branches depend on the value of a single variable.
Shell support case...esac statement which handles exactly this situation, and it does so more efficiently
than repeated if...elif statements.
Syntax:
The basic syntax of the case...esac statement is to give an expression to evaluate and several different
statements to execute based on the value of the expression.
The interpreter checks each case against the value of the expression until a match is found. If nothing
matches, a default condition will be used.
case word in
pattern1)
Statement(s) to be executed if pattern1 matches
;;
pattern2)
Statement(s) to be executed if pattern2 matches
;;
pattern3)
Statement(s) to be executed if pattern3 matches
;;
Esac
Here the string word is compared against every pattern until a match is found. The statement(s) following
the matching pattern executes. If no matches are found, the case statement exits without performing any
action.
When statement(s) part executes, the command ;; indicates that program flow should jump to the end of the
entire case statement. This is similar to break in the C programming language.
Example:
#!/bin/sh
FRUIT="kiwi"
case "$FRUIT" in
"apple") echo "Apple pie is quite tasty."
;;
"banana") echo "I like banana nut bread."
;;
"kiwi") echo "New Zealand is famous for kiwi."
;;
esac
LOOPS:
Loops are a powerful programming tool that enable you to execute a set of commands repeatedly. The
different types of loops available to shell programmers are:
You would use different loops based on different situation. For example while loop would execute given
commands until given condition remains true where as until loop would execute until a given condition
becomes true.
Nesting Loops:
All the loops support nesting concept which means you can put one loop inside another similar or
different loops. This nesting can go up to unlimited number of times based on your requirement.
Here is an example of nesting while loop and similar way other loops can be nested based on
programming requirement:
Nesting while Loops:
It is possible to use a while loop as part of the body of another while loop.
Syntax:
while command1 # this is loop1, the outer loop
do
Statement(s) to be executed if command1 is true
Example:
Here is a simple example of loop nesting, let's add another countdown loop inside the loop that you
used to count to nine:
#!/bin/sh
a=0
while [ "$a" -lt 10 ] # this is loop1
do
b="$a"
while [ "$b" -ge 0 ] # this is loop2
do
echo -n "$b "
b=`expr $b - 1`
done
echo
a=`expr $a + 1`
done
This will produce following result. It is important to note how echo -n works here. Here -n option let
echo to avoid printing a new line character.
0
1 0
2 1 0
3 2 1 0
4 3 2 1 0
5 4 3 2 1 0
6 5 4 3 2 1 0
7 6 5 4 3 2 10
8 7 6 5 4 3 210
9 8 7 6 5 4 3210
LOOP- CONTROL
a=10
while [ $a -ge 10 ]
do
echo $a
a=`expr $a + 1`
done
This loop would continue forever because a is alway greater than or equal to 10 and it would never
become less than 10. So this true example of infinite loop.
The break statement:
The break statement is used to terminate the execution of the entire loop, after completing the
execution of all of the lines of code up to the break statement. It then steps down to the code following
the end of the loop.
Syntax:
The following break statement would be used to come out of a loop:
break
The break command can also be used to exit from a nested loop using this format:
break n
Here n specifies the nth enclosing loop to exit from.
Example:
Here is a simple example which shows that loop would terminate as soon as a becomes 5:
#!/bin/sh
a=0
while [ $a -lt 10 ]
do
echo $a
if [ $a -eq 5 ]
then
break
fi
a=`expr $a + 1`
done
This will produce following result:
0
1
2
3
4
5
Here is a simple example of nested for loop. This script breaks out of both loops if var1 equals 2 and
var2 equals 0:
#!/bin/sh
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
This will produce following result. In the inner loop, you have a break command with the argument 2.
This indicates that if a condition is met you should break out of outer loop and ultimately from inner
loop as well.
10
15
The continue statement:
The continue statement is similar to the break command, except that it causes the current iteration of
the loop to exit, rather than the entire loop.
This statement is useful when an error has occurred but you want to try to execute the next iteration
of the loop.
Syntax:
continue
Like with the break statement, an integer argument can be given to the continue command to skip
commands from nested loops.
continue n
Here n specifies the nth enclosing loop to continue from.
Example:
The following loop makes use of continue statement which returns from the continue statement and
start processing next statement:
#!/bin/sh
NUMS="1 2 3 4 5 6 7"
Shell Substitutions
What is Substitution?
The shell performs substitution when it encounters an expression that contains one or more special
characters.
Example:
Following is the example, while printing value of the variable its substitued by its value. Same time "\n" is
substituted by a new line:
#!/bin/sh
a=10
echo -e "Value of a is $a \n"
This would produce following result. Here -e option enables interpretation of backslash escapes.
Value of a is 10
Value of a is 10\n
Here are following escape sequences which can be used in echo command:
Escape Description
\\ Backslash
\a alert (BEL)
\b Backspace
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
Command Substitution:
Command substitution is the mechanism by which the shell performs a given set of commands and then
substitutes their output in the place of the commands.
Syntax:
`command`
When performing command substitution make sure that you are using the backquote, not the single quote
character.
Example:
Command substitution is generally used to assign the output of a command to a variable. Each of the
following examples demonstrate command substitution:
#!/bin/sh
DATE=`date`
echo "Date is $DATE"
USERS=`who | wc -l`
echo "Logged in user are $USERS"
UP=`date ; uptime`
echo "Uptime is $UP"
Variable Substitution:
Variable substitution enables the shell programmer to manipulate the value of a variable based on its state.
Form Description
The Metacharacters:
Unix Shell provides various metacharacters which have special meaning while using them in any Shell Script
and causes termination of a word unless quoted.
For example ? matches with a single charater while listing files in a directory and an * would match more
than one characters. Here is a list of most of the shell special characters (also called metacharacters):
* ? [ ] ' " \ $ ; & ( ) | ^ < > new-line space tab
A character may be quoted (i.e., made to stand for itself) by preceding it with a \.
Example:
#!/bin/sh
Hello
./[Link]: line 2: Word: command not found
#!/bin/sh
Hello; Word
The $ sign is one of the metacharacters, so it must be quoted to avoid special handling by the shell:
#!/bin/sh
I have $1200
Quoting Description
Single quote All special characters between these quotes lose their special meaning.
Double quote Most special characters between these quotes lose their special meaning with
these exceptions:
$
`
\$
\'
\"
\\
Backslash Any character immediately following the backslash loses its special meaning.
Back Quote Anything in between back quotes would be treated as a command and would be
executed.
Putting a backslash in front of each special character is tedious and makes the line difficult to read:
There is an easy way to quote a large group of characters. Put a single quote ( ') at the beginning and at the
end of the string:
Any characters within single quotes are quoted just as if a backslash is in front of each character. So now this
echo command displays properly.
If a single quote appears within a string to be output, you should not put the whole string within single
quotes instead you whould preceed that using a backslash (\) as follows:
VAR=ZARA
echo '$VAR owes <-$1500.**>; [ as of (`date +%m/%d`) ]'
VAR=ZARA
echo "$VAR owes <-\$1500.**>; [ as of (`date +%m/%d`) ]"
Double quotes take away the special meaning of all characters except the following:
Any characters within single quotes are quoted just as if a backslash is in front of each character. So now this
echo command displays properly.
If a single quote appears within a string to be output, you should not put the whole string within single
quotes instead you whould preceed that using a backslash (\) as follows:
Syntax:
Here is the simple syntax to put any Shell command in between back quotes:
Example:
var=`command`
Example:
Following would execute date command and produced result would be stored in DATA variable.
DATE=`date`
Shell Functions
Functions enable you to break down the overall functionality of a script into smaller, logical subsections,
which can then be called upon to perform their individual task when it is needed.
Using functions to perform repetitive tasks is an excellent way to create code reuse. Code reuse is an
important part of modern object-oriented programming principles.
Shell functions are similar to subroutines, procedures, and functions in other programming languages.
Creating Functions:
To declare a function, simply use the following syntax:
function_name () {
list of commands
}
The name of your function is function_name, and that's what you will use to call it from elsewhere in your
scripts. The function name must be followed by parentheses, which are followed by a list of commands
enclosed within braces.
Example:
#!/bin/sh
When you would execute above script it would produce following result:
[um_ramya@ssh ~]$./[Link]
Hello World
[um_ramya@ssh ~]$
Following is an example where we pass two parameters Zara and Ali and then we capture and print these
parameters in the function.
#!/bin/sh
[um_ramya@ssh ~]$./[Link]
Hello World Zara Ali
[um_ramya@ssh ~]$
If you instead want to just terminate execution of the function, then there is way to come out of a defined
function.
Based on the situation you can return any value from your function using the return command whose
syntax is as follows:
return code
Here code can be anything you choose here, but obviously you should choose something that is meaningful
or useful in the context of your script as a whole.
Example:
#!/bin/sh
[um_ramya@ssh ~]$./[Link]
Hello World Zara Ali
Return value is 10
[um_ramya@ssh ~]$
Nested Functions:
One of the more interesting features of functions is that they can call themselves as well as call other
functions. A function that calls itself is known as a recursive function.
#!/bin/sh
number_two () {
echo "This is now the second function speaking..."
}
Alternatively, you can group the definitions in a file, say [Link], and then execute the file in the current shell
by typing:
[um_ramya@ssh ~]$. [Link]
This has the effect of causing any functions defined inside [Link] to be read in and defined to the current
shell as follows:
To remove the definition of a function from the shell, you use the unset command with the .f option. This is
the same command you use to remove the definition of a variable to the shell.