Error Trapping In Bash Script
Contents |
and Signals and Traps (Oh My!) - Part 1 by William Shotts, Jr. In this lesson, we're going to look at handling errors during the execution of your scripts. The difference between a good program and a poor one bash script trap ctrl c is often measured in terms of the program's robustness. That is, the program's ability bash throw error to handle situations in which something goes wrong. Exit status As you recall from previous lessons, every well-written program returns an bash quit on error exit status when it finishes. If a program finishes successfully, the exit status will be zero. If the exit status is anything other than zero, then the program failed in some way. It is very
Shell Script Error Handling
important to check the exit status of programs you call in your scripts. It is also important that your scripts return a meaningful exit status when they finish. I once had a Unix system administrator who wrote a script for a production system containing the following 2 lines of code: # Example of a really bad idea cd $some_directory rm * Why is this such a bad way of doing it? shell script catch error It's not, if nothing goes wrong. The two lines change the working directory to the name contained in $some_directory and delete the files in that directory. That's the intended behavior. But what happens if the directory named in $some_directory doesn't exist? In that case, the cd command will fail and the script executes the rm command on the current working directory. Not the intended behavior! By the way, my hapless system administrator's script suffered this very failure and it destroyed a large portion of an important production system. Don't let this happen to you! The problem with the script was that it did not check the exit status of the cd command before proceeding with the rm command. Checking the exit status There are several ways you can get and respond to the exit status of a program. First, you can examine the contents of the $? environment variable. $? will contain the exit status of the last command executed. You can see this work with the following: [me] $ true; echo $? 0 [me] $ false; echo $? 1 The true and false commands are programs that do nothing except return an exit status of zero and one, respectively. Using them, we can see how the $? environment
and Signals and Traps (Oh, My!) - Part 2 by William Shotts, Jr. Errors are not the only way that a script can terminate unexpectedly. You also have to be shell script exit code concerned with signals. Consider the following program: #!/bin/bash echo "this script will
Shell Script Exit On Error
endlessly loop until you stop it" while true; do : # Do nothing done After you launch this script
Bash If Exit Code
it will appear to hang. Actually, like most programs that appear to hang, it is really stuck inside a loop. In this case, it is waiting for the true command to http://linuxcommand.org/wss0150.php return a non-zero exit status, which it never does. Once started, the script will continue until bash receives a signal that will stop it. You can send such a signal by typing ctrl-c which is the signal called SIGINT (short for SIGnal INTerrupt). Cleaning up after yourself OK, so a signal can come along and make your script terminate. Why does it http://linuxcommand.org/wss0160.php matter? Well, in many cases it doesn't matter and you can ignore signals, but in some cases it will matter. Let's take a look at another script: #!/bin/bash # Program to print a text file with headers and footers TEMP_FILE=/tmp/printfile.txt pr $1 > $TEMP_FILE echo -n "Print file? [y/n]: " read if [ "$REPLY" = "y" ]; then lpr $TEMP_FILE fi This script processes a text file specified on the command line with the pr command and stores the result in a temporary file. Next, it asks the user if they want to print the file. If the user types "y", then the temporary file is passed to the lpr program for printing (you may substitute less for lpr if you don't actually have a printer attached to your system.) Now, I admit this script has a lot of design problems. While it needs a file name passed on the command line, it doesn't check that it got one, and it doesn't check that the file actually exists. But the problem I want to focus on here is the fact that when the script t
Bash Prompts About Writing Robust Bash Shell Scripts Many people hack together shell scripts quickly to do simple tasks, but these soon take on a life of their own. Unfortunately shell scripts are full of subtle effects which result in scripts http://www.davidpashley.com/articles/writing-robust-shell-scripts/ failing in unusual ways. It's possible to write scripts which minimise these problems. In this article, I explain several techniques for writing robust bash scripts. Use set -u How often have you written a script that broke because a variable wasn't set? I know I have, many times. chroot=$1 ... rm -rf $chroot/usr/share/doc If you ran the script above and accidentally forgot to give a parameter, you would have just deleted shell script all of your system documentation rather than making a smaller chroot. So what can you do about it? Fortunately bash provides you with set -u, which will exit your script if you try to use an uninitialised variable. You can also use the slightly more readable set -o nounset. david% bash /tmp/shrink-chroot.sh $chroot= david% bash -u /tmp/shrink-chroot.sh /tmp/shrink-chroot.sh: line 3: $1: unbound variable david% Use set -e Every script you write should include set shell script exit -e at the top. This tells bash that it should exit the script if any statement returns a non-true return value. The benefit of using -e is that it prevents errors snowballing into serious issues when they could have been caught earlier. Again, for readability you may want to use set -o errexit. Using -e gives you error checking for free. If you forget to check something, bash will do it or you. Unfortunately it means you can't check $? as bash will never get to the checking code if it isn't zero. There are other constructs you could use: command if [ "$?"-ne 0]; then echo "command failed"; exit 1; fi could be replaced with command || { echo "command failed"; exit 1; } or if ! command; then echo "command failed"; exit 1; fi What if you have a command that returns non-zero or you are not interested in its return value? You can use command || true, or if you have a longer section of code, you can turn off the error checking, but I recommend you use this sparingly. set +e command1 command2 set -e On a slightly related note, by default bash takes the error status of the last item in a pipeline, which may not be what you wan