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.
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.
$ test -r myfile $ echo $? 0
How does one interpret the return code?
not 0 (not zero)
$ 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.
$ [ -r myfile ] # note the space after the left bracket $ echo $? 0
The if statement in Unix is the basic two-way branch.
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).
#!/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.
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.
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.
Many computer languages support a multi-way branch. Unix is included as one of them.
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.
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 $
case match_string_expr in match_pattern) action1;; match_pattern) action2;; ...
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.
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.
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.
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.
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.
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.
./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.
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.
#!/bin/bash #comments #comments code begins here
#comments #comments #!/bin/bash code begins here
code begins here