Shell Programming Deep Dive
Shell scripts in Linux are an easy to use time saving solution for automating repetitive or complex set of tasks in a System Administrators daily life. It can help save time and increase productivity. So it’s a must have skill for any one working on computers.
Conditional Statements
if Statements
The core conditional concept of wrapping a block of statements that is only to be processed if some condition is met. Shells also support else and the combined elif else-if condition, 3 basic layouts for if statements are shown below. Note the use of the then and keyword to separate the condition commands from internal command block, and the fi keyword to mark the end of the block.
First Model
if condition-command
then
command1
command2
...
fi
Second Model
if condition-command
then
commandA1
commandA2
...
else
commandB1
commandB2
...
fi
Third Model
if condition-command-A
then
commandA1
commandA2
...
elif condition-command-B
then
commandB1
commandB2
...
else
commandC1
commandC2
...
fi
The commands inside the blocks are used the same as any other command within the system, and it is possible to nest other conditions statements inside those blocks. For conciseness, many people will use the semicolon (;) command separating character to allow the then keyword to be placed on the same line as the if and the condition-command. (Note that no semicolon is needed after the then keyword) The condition-command is a any command, and the if statement is evaluated based on the success (exit status) of that command.
Example:
if ls -al ; then
echo "Directory was successfully listed"
else
echo "Failed to list directory"
fi
The test or [ ] Command
There are other types of tests beyond running a simple command and checking its exit status. The shells support a number for “test” commands that can be used to perform more useful tests. The test command has two formats:
test expression
or
[ expression ]
Note: for the [ ] version there must be spaces between the brackets and the expression. The test command/statement will evaluate the expression and return a status of success if the expression is true. The following tests can be performed:
File Tests
Example:
true if file is readable (by current user) true if file is writable (by current user) true if file is executable (by current user) true if file is an ordinary file true if file is a directory true if the length of file is > 0 true if standard output is associated with a terminal true if file descriptor ‘fd’ is associated with a terminal would be where a program needs to output something to a file, but first checks that the file exists:
#!/bin/sh
echo “Enter the filename”
read file1
if [ ! -s file1 ] or if test ! –s file1
then
echo "file1 is empty or does not exist."
ls -l > file1
exit
else
echo "File file1 already exists."
fi exit 0
String Tests
An example would checks that the given strings are same or not:
#!/bin/sh
echo “Enter the strings(string1,string2)”
read str1
read str2
if test str1 = str2
then
echo "Both Strings are equal"
exit else
echo "Given strings are not equal"
fi
exit 0
Numeric Tests
Example
!/bin/sh
if test $# -le 5
then
echo Less than or equal to five parameters.
else
echo More than 5 parameters.
fi
exit 0
Combining Operators
- ! - NOT - Invert the result of the next expression
- -a - AND - Only true if both prev and next expr are true OR - True if either prev or next expr is true
- -o - OR - True if either prev or next expr is true
- ( ) - parentheses for grouping expressions
All parameters to test must be separated by spaces, including parentheses, operators and strings/integers. Note, that test operates on strings (or strings containing integer numbers) It cannot process commands directly, we must use the back-quotes to perform command substitution.
if [ "$HOME" = `pwd` ] ; then echo "I am home" ; fi
Also double quoting of variable is important within a test statement, as an undefined variable will resolve to nothing which will not be correctly processed by the shell.
For example, if $HOME is undefined
if [ $HOME = `pwd` ] ; then...
will expand to be:
if [ = /export/home/me ] ; then...
Which is invalid.
case Statements
When there are several different sets of operations to be performed based on different values of a single string, it can get quite messy using a long string of if, elif, elif, elif, else statements to perform the operations. The case command allows a convenient structured method of performing flow control through one of multiple blocks using pattern matching of strings.
case string in
pattern1 ) commands;;
pattern2 ) commands ;;
pattern3 ) commands ;;
...
*) commands ;; # default case
esac
In addition, multiple pattern options can be specified for a single case using pattern1a | [I pattern1b] style blocks. When specifying patterns,
*
matches any string of zero or more characters- ? matches any single character
- matches any single character within the set
- [! ] matches any single character not in the set
These can be combined together to form more advanced patterns: [Yy]* Matches any string starting with an upper or lower case y.
while Statements
Similar to the basic if statement, except the block of commands is repeatedly executed as long as the condition is met.
while condition-command
do
command1
command2
...
done
As with if statements, a semicolon (;) can be used to remove include the do keyword on the same line as the while condition-command statement.
The example below loops over two statements as long as the variable i is less than or equal to ten. Store the following in a file named while1.sh and execute it
!/bin/sh
Illustrates implementing a counter with a while loop Notice how we increment the counter with expr in backquotes
i="1"
while [ $i -le 10 ]
do
echo "i is $i"
i=`expr $i + 1`
done
until loop
Execute statements as long as a condition is false
until grep "sort" dbase_log > /dev/null
do
sleep 10 done
echo "Database has been sorted"
Example executes until grep is unsuccessful
for Statement
The for statement is used to iterate/loop through a predefined list of values, setting a shell variable to the current value each time through the loop. Unlike many other languages shell for loops operate on word lists, not integers with start and stop values.
for VAR in wordlist
do
commands
... done
Example
#!/bin/sh
for i in Claude Paul Wayne Roger Tom
do
echo The name is $i.
done
exit 0
Within the shell, parameters are read as $1 for the first parameter, $2 for the second parameter, $3 for the third parameter, and so on. $* is the entire list of parameters. If the ``in list" is omitted, the list taken is the list of parameters passed to the shell on the command line. Note: To excite above said example programs we have to compile them in a file and excite that file. Like
sh <filename>
Debugging
The simplest debugging help is of course the command echo. We can use it to print specific variables around the place where we suspect the mistake. This is probably what most shell programmers use 80% of the time to track down a mistake. The advantage of a shell script is that it does not require any re-compilation and inserting an “echo” statement is done very quickly. The shell has a real debug mode as well. If there is a mistake in our script “strangescript” then we can debug it like this:
sh -x strangescript
This will execute the script and show all the statements that get executed with the variables and wildcards already expanded. The shell also has a mode to check for syntax errors without actually executing the program. To use this run:
sh -n our_script
If this returns nothing then our program is free of syntax errors.