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.
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.
if condition-command then command1 command2 ... fi
if condition-command then commandA1 commandA2 ... else commandB1 commandB2 ... fi
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.
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:
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
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
!/bin/sh if test $# -le 5 then echo Less than or equal to five parameters. else echo More than 5 parameters. fi exit 0
- ! - 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.
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
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.
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
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
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
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
#!/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
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.