Pitfalls of shell scripting. Part2
- General programming languages provide the ability to add conditions after the if command, but in shell scripting one cannot give conditions as it is.
- It has to be a command and the EXIT VALUE of the command is used in the if condition testing.
- If the command returned a 0, meaning successful completion, it goes to the "then" block, for all other VALUES, it moves to the next line of the "then" statement.
- There can be more than one commands on the if line, and all of them are executed in the order they are specified but the exit code of the last command alone is used to check the "if" condition. This can be a source of common error.
- There can be an "elif" statement(equivalent to the generic-else if), an "else" statement(which should, of course be the last conditional).
- But there has to be a "fi" statement at the end of the "if" block.
- One cannot put simple checks, like we do in other programming languages, on the "if" line. It has to be a command. So for operations like
$var1 is greater than $var2,
we have to use the test command, which evaluates an expression and stores the true value as 0, and false as 1.
So we have to use this command to get around checks in the "if" block.
if test $var1 -gt $var2
- There is a better substitute for the test command, which reduces typing and makes code more readable. [ ] operator is of use here.
if [ $var -gt $var2 ]
- One hugely important point to note here that there should be a space after the opening brace and a space between the last character and the closing brace. This is a huge source of common error.
There are 3 classes of tests available in UNIX shell scripting.
1. Numeric comparisons
|Less Than||-lt||$var1 -lt $var2|
|Greater Than||-gt||$var1 -gt $var2|
|Less Than Equal to||-le||$var1 -le $var2|
|Greater Than Equal to||-ge||$var1 -ge $var2|
|Equal to||-eq||$var1 -eq $var2|
|Not Equal to||-ne||$var1 -ne $var2|
- One notable shortcoming of the bash shell is that it cannot handle anything other than integers. So this operation would give an error.
var1=`bc << 10/3`
if [ $var1 -gt 3 ]
It will give an error in the comparison statement as var1 is 3.33 and it expects an integer. And there is no work-around this. :-(
|Greater Than||>||$var1 > $var2|
|Less Than||<||$var1 < $var2|
|Equal to||=||$var1 = $var2|
|Not Equal to||!=||$var1 != $var2|
- Note that the equality test is only a single "=" unlike most other programming languages.
- Also note that one has to escape the ">" & "<" characters with the normal backslash(\) otherwise the shell treats them as the file redirection operators.
There are 2 handy operations available for strings
-n = Tests if a string has length greater than 0.
-z = Tests if a string has 0 length.
if [ -n $var1 ] # Returns true
if [ -z $var2 ] # Returns true
3. File comparisons
This kind of comparison is useful for files and directories manipulation
|Check if file exists and is a file||-f||-f file1|
|Check if file exists and is a directory||-d||-d file1|
|Check if file exists||-e||-e file1|
|Check if file is writable||-w||-w file1|
|Check if file is readable||-r||-r file1|
|Check if file is executable||-x||-x file1|
|Check if file1 is newer than file2||-nt||file1 -nt file2|
|Check if file1 is older than file2||-ot||file1 -ot file2|
Compound condition testing
The normal [ ] style can also handle multiple testing operations.
eg., [ $var1 -gt $var2 ] && [ $var3 -le $var4 ]
The curious reader might observe that it comes handy while operating on files as one would like to test if a file exists and is writable before attempting to write to it. This makes the program more robust as it fails hard and loudly when something is not as it expected, which is a desirable design policy.
eg., if [ -f $file1 ] && [ -w $file1 ]
checks if the file in variable file1 is a file and exists and is writable?
There are 2 variations of the [ ] operation.
1. [[ ]] This is useful for string comparisons because it provides the ability for pattern matching.
eg., if [[ -e /folder/file* ]]
2. (( )) this is useful for numeric comparisons as it can perform complex operations like **(exponentiation), <<, >> (bitwise operations for shifting), &, | (bitwise operations for logical comparisons) etc.
the syntax for case command is
case variable in
pattern1 | pattern2 ... | patternN) command_Set1;;
*) default commands;;
One good thing is that one can list multiple cases separating them with the "|" operator together.
case $v in
[0-5])echo lower half nos
echo "skipping the rest";;
[5-8])echo higher half nos
echo "skipping the rest";;
*)echo "It is 9!!!!";;
Observe that the set of commands are to be listed as is. the ";;" symbol is to appear only at the end of the command set.