Flow Control - Part 1
In this lesson, we will look at how to add intelligence to our scripts. So far, our script has only consisted of a sequence of commands that starts at the first line and continues line by line until it reaches the end. Most programs do more than this. They make decisions and perform different actions depending on conditions.The shell provides several commands that we can use to control the flow of execution in our program. These include:
- if
- exit
- for
- while
- until
- case
- break
- continue
if
The first command we will look at is if. The if command is fairly simple on the surface; it makes a decision based on a condition. The if command has three forms:# First form if condition ; then commands fi # Second form if condition ; then commands else commands fi # Third form if condition ; then commands elif condition ; then commands fi
In the second form, if the condition is true, then the first set of commands is performed. If the condition is false, the second set of commands is performed.
In the third form, if the condition is true, then the first set of commands is performed. If the condition is false, and if the second condition is true, then the second set of commands is performed.
What is a "condition"?
To be honest, it took me a long time to really understand how this worked. To help answer this, there is yet another basic behavior of commands we must discuss.Exit status
A properly written Unix application will tell the operating system if it was successful or not. It does this by means of an exit status. The exit status is a numeric value in the range of 0 to 255. A "0" indicates success; any other value indicates failure. Exit status provides two important features. First, it can be used to detect and handle errors and second, it can be used to perform true/false tests.It is easy to see that handling errors would be valuable. For example, in our script we will want to look at what kind of hardware is installed so we can include it in our report. Typically, we will try to query the hardware, and if an error is reported by whatever tool we use to do the query, our script will be able to skip the portion of the script which deals with the missing hardware.
We can also use the exit status to perform simple true/false decisions. We will cover this next.
test
The test command is used most often with the if command to perform true/false decisions. The command is unusual in that it has two different syntactic forms:# First form test expression # Second form [ expression ]
The neat feature of test is the variety of expressions you can create. Here is an example:
if [ -f .bash_profile ]; then echo "You have a .bash_profile. Things are fine." else echo "Yikes! You have no .bash_profile!" fi
Here is a partial list of the conditions that test can evaluate. Since test is a shell builtin, use "help test" to see a complete list.
Expression |
Description |
-d file |
True if file is a directory. |
-e file |
True if file exists. |
-f file |
True if file exists and is a
regular file. |
-L file |
True if file is a symbolic
link. |
-r file |
True if file is a file readable by
you. |
-w file |
True if file is a file writable by
you. |
-x file |
True if file is a file executable
by you. |
file1 -nt file2 |
True if file1 is newer than
(according to modification time)
file2 |
file1 -ot file2 |
True if file1 is older than
file2 |
-z string |
True if string is empty. |
-n string |
True if string is not empty. |
string1 = string2 |
True if string1 equals
string2. |
string1 != string2 |
True if string1 does not equal
string2. |
In the first line of the script, we see the if command followed by the test command, followed by a semicolon, and finally the word then. I chose to use the [ expression ] form of the test command since most people think it's easier to read. Notice that the spaces between the "[" and the beginning of the expression are required. Likewise, the space between the end of the expression and the trailing "]".
The semicolon is a command separator. Using it allows you to put more than one command on a line. For example:
[me@linuxbox me]$ clear; ls
will clear the screen and execute the ls
command.I use the semicolon as I did to allow me to put the word then on the same line as the if command, because I think it is easier to read that way.
On the second line, there is our old friend echo. The only thing of note on this line is the indentation. Again for the benefit of readability, it is traditional to indent all blocks of conditional code; that is, any code that will only be executed if certain conditions are met. The shell does not require this; it is done to make the code easier to read.
In other words, we could write the following and get the same results:
# Alternate form if [ -f .bash_profile ] then echo "You have a .bash_profile. Things are fine." else echo "Yikes! You have no .bash_profile!" fi # Another alternate form if [ -f .bash_profile ] then echo "You have a .bash_profile. Things are fine." else echo "Yikes! You have no .bash_profile!" fi
exit
In order to be good script writers, we must set the exit status when our scripts finish. To do this, use the exit command. The exit command causes the script to terminate immediately and set the exit status to whatever value is given as an argument. For example:exit 0
exit 1
Testing for root
When we last left our script, we required that it be run with superuser privileges. This is because the home_space function needs to examine the size of each user's home directory, and only the superuser is allowed to do that.But what happens if a regular user runs our script? It produces a lot of ugly error messages. What if we could put something in the script to stop it if a regular user attempts to run it?
The id command can tell us who the current user is. When executed with the "-u" option, it prints the numeric user id of the current user.
[me@linuxbox me]$ id -u
501
[me@linuxbox me]$ su
Password:
[root@linuxbox me]# id -u
0
If the superuser executes id
-u, the command will output "0." This fact can
be the basis of our test:501
[me@linuxbox me]$ su
Password:
[root@linuxbox me]# id -u
0
if [ $(id -u) = "0" ]; then echo "superuser" fi
While this code will detect if the user is the superuser, it does not really solve the problem yet. We want to stop the script if the user is not the superuser, so we will code it like so:
if [ $(id -u) != "0" ]; then echo "You must be the superuser to run this script" >&2 exit 1 fi
Notice the ">&2" at the end of the echo command. This is another form of I/O direction. You will often notice this in routines that display error messages. If this redirection were not done, the error message would go to standard output. With this redirection, the message is sent to standard error. Since we are executing our script and redirecting its standard output to a file, we want the error messages separated from the normal output.
We could put this routine near the beginning of our script so it has a chance to detect a possible error before things get under way, but in order to run this script as an ordinary user, we will use the same idea and modify the home_space function to test for proper privileges instead, like so:
function home_space { # Only the superuser can get this information if [ "$(id -u)" = "0" ]; then echo "<h2>Home directory space by user</h2>" echo "<pre>" echo "Bytes Directory" du -s /home/* | sort -nr echo "</pre>" fi } # end of home_space
0 comments:
Post a Comment
Note: only a member of this blog may post a comment.