The most common flow of control structure is the if statement. We have already seen a simple version of this in the command line delimiters && (and) and || (or). Both of these test the return status of the preceding command before deciding on whether the succeeding command is to be executed.
&& and || are intended for fairly simple decissions involving a few commands on a single command line. However, bash supports the "if, then" keyword structure which allows you to program very complex decission structures involving many commands. Like && and ||, "if" tests the status of a specified command or command sequence for success or failure.
Unlike conditional statements in programming languages which perform some comparision between two pieces of information, "if" invokes the specified command and tests its return status. The command run may compare two pieces of information and set an appropriate return status or it may perform its designated activity, such as moving a file. Upon completion, the command returns a status. The "if" then acts on that returned status.
Exercise : |
if command[s]_to_run then action_command[s] # run if command[s]_to_run is successful fi
The exercise above could have also been entered as follows :
if ls; then echo FOUND; fi # note that ; should not follow "then"
The number of command[s]_to_run is only limited by system parameters such as size of command line buffer. It is possible to enter several commands for "if" to invoke and test as long as you use the standard delimiters between commands. These include semi-colon, && and, || or, and | pipe. Parenthesis can also be used.
&, the background delimiter may be used, but "if" will test if the command was successfully placed in the background, not the status returned when the command terminates.
The following tests for existence of a file's backup, and if it does not exit, makes a backup of the specified file and restricts access to backup. The 2>&nbs;/dev/null disposes of any error message generated by ls. The ! inverts the return code of the command following it. ! must have space on either side. !ls would be processed as a history reference.
# first create a file to play with (name of your choice) ls > file1 if ! ls file1.bak 2> /dev/null && cp file1 file1.bak && chmod 000 file1.bak then echo "backup good" fi # Scroll up or use !! (history : recall last command entered) to re-run # the if. This time you should see the backup listed and no message.
All of the commands specified will run unless the delimiters restrict them.
The if structure supports multiple alternatives test/action sets with the elif and the else keywords.
if cmd1 then action1 else action2 fi if cmd1 then action1 elif cmd2 # must be paired with following then then action2 fi if cmd1 then action1 elif cmd2 then action2 else action3 fi
There is a single "if", there may be any number of "elif" statements, and there may be one "else" as the last conditional.
"if" also allows embedded "if"s within the action block of the statement.
if cmd1 then if cmdA then action1 elif cmdB then action2 else action3 fi else some_other_action fi
When designing an "if" that tests multiple conditions, place dissimilar tests in their own "if" block. For example, if you want to compile a program but only if exists and is readable and the target directory is writable. Rather than a single if with multiple tests, test access of the target directory and in the action block, code another "if" that tests the access permission of the file to compile.
# Example # Line spacing and indenting help readability and will not affect # execution. # test access to target directory if test -d target_dir -a -w target_dir -a -x target_dir then # test access to file being compiled if test -r progam.c then gcc progam.c -o target_dir/runnable else echo "program.c does not exist or is not readable" fi else echo "No access to target directory" fi
This simplifies each test condition and allows you to introduce specific error messages in the appropriate else section.
Although "if" does not do comparisons, it is commonly used to invoke the "test" command which can. There are generally two test commands available from a shell script.
The first is the standard test program. This is an external program which can be invoked by "if". To use it, specify it by its full path name. To find the path, use :
whereis test
An example of using the external command :
if /bin/test "$value" -gt 100; then echo "exceeds value"; fi
Use the external version when you may be coding shell scripts in several different shells, such as bash and tcsh. By using the external version, the same test is performed.
The second version of test is part of the shell interpreter running the script. Most shells, bash, bourne, csh, tcsh, etc., are designed with an internal version of the test utility. The advantage of an internal version is that it runs faster and does not consume the system resources that invoking another program would. Additionally, the internal versions often offer more varieties of test or easier syntax.
Use the internal version when you plan on only coding in a particular shell or you desire the speed or convenience of that shell's internal test. But remember that the syntax may not be available if you have to code the same test in a different shell.
See the module on the test command for specifics on test.