Main Body
5 Control Structures – Part 1 – branching
In order to support control structures (branching, looping, etc.), all computer languages need some method of evaluating a condition. In Unix, the command to evaluate a condition is the test command. The test command is often at the heart of most control structures in Unix.
test
What does the test command do? It evaluates a condition and sets the special shell variable $?, the return code. Much to the confusion of new users, the test command is silent in that it does not print anything to standard output. Thus when running the test command, it appears as if to do nothing. To check the return code, one may simply print its value with an echo statement.
Example 1: Check if a file is readable.
$ test -r myfile $ echo $? 0
How does one interpret the return code?
Unix convention:
0 (zero) |
means TRUE |
---|---|
not 0 (not zero) |
means FALSE |
Example 2: Compare if two strings are equal.
$ test "this" = "that" $ echo $? 1
Synonym to the test command: [
For reasons of readability, many programmers with use the synonym or abbreviation for the test command which is the left square bracket: [ . In all examples above, replace the word test with the left square bracket. Note that a matching right square bracket needs to be added for syntactic reasons. As with all Unix commands, spaces are a big deal and a space is required after the left square bracket and before the right square bracket.
Example 3: Synonym to the test command [
$ [ -r myfile ] # note the space after the left bracket $ echo $? 0
Control Structures
if
The if statement in Unix is the basic two-way branch.
Syntax
if unix_statements then actions_true else # optional actions_false fi
How does the if statement work? Here is the sequence of operations. The if statement will:
- Run all the unix_statements.
- Check the return code ($?) of the last statement in the list (just prior to the keyword “then”.
- If the return code is true. the “then” clause is executed (
actions_true). If the return code is false, the “else” clause is executed (actions_false). The else clause is optional (can be left out if not needed).
Example of if
#!/bin/bash comfort=20 temperature=18 if echo The current temperature is $temperature echo Temperature for comparison is $comfort [ $temperature -lt $comfort ] then echo It is cold. else echo It is warm. fi
Nested conditions are supported in Unix. Any statement in the “then” or “else” clause can itself be another if statement.
elif
Although the if statement is primarily a two-way branch (e.g. true or false), a multi-way branch (e.g. red, yellow, green) can be coded using a set of nested if statements. Some computer languages support an “else-if”-type clause; Unix is one of them.
There is an “else-if” clause called “elif” which requires a statement just like the if clause.
Example: elif
if [ $temperature -lt $comfort ] then echo It is cold. elif [ $temperature -eq $comfort ] then echo It is perfect. else echo It is warm. fi
Note that the “elif” clause is actually a clause of the main “if” and not a nested if statement. Thus, there is only one “fi” (end if) required for each opening “if” regardless of how many “elif” clauses there are.
case
Many computer languages support a multi-way branch. Unix is included as one of them.
Simplified syntax:
case $variable in val1) action1;; val2) action2;; *) default action;; esac
The keywords are case, in, and esac. The double semi-colon is a syntactic requirement to separate the inner clauses of the case statement.
Example: Flexible command line processing.
Allow your user to run your script in various ways. Accommodate all of the following invocations.
$ ./myscript # user omits filename; give second chance $ ./myscript chapter3 # preferred syntax; just proceed $ ./myscript chapter3 chapter5 # multiple arguments not supported; inform user Place this code snippet at the beginning of your script like this: $ cat myscript case $# in 0) echo Enter file name: read arg1;; 1) arg1=$1;; *) echo invalid number of arguments echo "Syntax: $0 filename" exit 1;; esac # rest of program continues after esac $
General syntax:
case match_string_expr in match_pattern) action1;; match_pattern) action2;; ...
esac
Example: Demonstrate pattern matching use in case statement. Print a message about the length of the current month.
lhiraki@metis:~/test$ cat case_month case $(date '+%m') in 01|03|05|07|08|10|12) echo This is a long month;; 04|06|09|11) echo This is a short month;; 02) echo This is the shortest month;; *) echo Something wrong with date command;; esac # Example run in September lhiraki@metis:~/test$ ./case_month This is a short month lhiraki@metis:~/test$
The date command is called with an option to return the numerical value of the month (e.g. Jan=01, Feb=02, etc.). Depending on the month, a message is printed regarding the length of the month. Months with the same number of days are grouped together using a pattern with the OR (vertical bar) syntax.
Defensive programming
Well the previous example is rather trivial and the date command has been well tested over the years, it is considered good practice to always have a default clause even if you think you have covered all possible conditions.
Summary
The match_string_expr is matched against each match_pattern in the order coded. At the first match, the corresponding action is taken. After one action is completed, the case statement terminates and execution continues after the esac (end case). There is no “fall-through”. The case statement will not execute multiple actions.
shift
When processing multiple command-line arguments, it may be necessary to manipulate them to facilitate processing.
The shift command moves all command-line arguments one position to the left. For example the second command line argument is moved into the first position; the third command line argument is moved into the second position, and so forth.
Example of shift command
lhiraki@thebe:~/test$ cat shifter #!/bin/bash echo The 1st arg is $1 echo The 2nd arg is $2 shift echo The 2nd arg is $1 shift echo The 3rd arg is $1
Execution results in:
lhiraki@thebe:~/test$ ./shifter apple pear grape The 1st arg is apple The 2nd arg is pear The 2nd arg is pear The 3rd arg is grape lhiraki@thebe:~/test$
The exit command
The exit command does two things:
- It terminates the current shell (or script), returning control to the calling shell, if any.
- It sets the return code ($?) for your script.
Example – exit command usage
The trivial but illustrative script exit_example shows the exit command setting return code for the script to 3 and then terminating the script. Control returns to the calling program, in this case just back to the operating system prompt.
$ cat exit_example #!/bin/bash exit 3 echo This line never executed. $ ./exit_example $ echo $? 3 $
Note that one must inspect the return code ($?) immediately after running exit_example. The return code is updated (overwritten) by each Unix command executed.
The exit command is typically used to terminate a script midway through often due to an errror condition. The other primary use of the exit command is for a sub-script to communicate information back to the calling script.
Example – parent/child script relation
parent |
child |
---|---|
./child ret_val=$? case $ret_val |
... exit 2 ... exit 0 |
Note: parent-child relationship can be used for team project where multiple people can work on the same file at the same time.
Shebang line
To specify which interpreter Unix should use when executing your script, as the first line of the file place the path to the interpreter after “#!”. While the number sign character (#) normally introduces a comment, when used with the exclamation mark at the beginning of a file, Unix will load the interpreter specified in the path to run the rest of the script file.
This is especially important to make your script portable in Unix environments. If you write your script in bash, and you give your script for someone else to use who works in a c-shell or Korn shell environment, your script may not work properly. To ensure that the script is run under bash, you must specify the shebang line.
Careful: The shebang line must be the first line of the file, not just the first line of text, or the first line of code. A common mistake is to have a blank line as the first line, or some comments above the shebang line. Unix does not look beyond the first line of the file in order to identify the expected interpreter.
Right |
Wrong |
Wrong |
---|---|---|
#!/bin/bash #comments #comments code begins here |
#comments #comments #!/bin/bash code begins here |
#!/bin/bash
code begins here
|