Here are two simple tricks that are easy to use and will make your scripts much more robust.
Turn on -e mode (do you feel lucky - punk?)
In this mode any command your script runs which returns a non-zero exitcode - an error in the world of shell - will cause your script to itself terminate immediately with an error.
You can do that in your shebang line:
#!/bin/sh -e
Or using set:
set -e
Yes, this is what you want. A neat predictable failure is infinitely better than a noisy unreliable failure.
If you REALLY want to ignore an error, be explicit about it:
# I don't care if evil-broken-command fails
evil-broken-command || true
Oh and as long as you're messing with shell modes, -e goes well with -x (which I like to think of as shell X-ray).
Like this:
#!/bin/sh -ex
Or like this:
# turn -x on if DEBUG is set to a non-empty string
[ -n "$DEBUG" ] && set -x
That way you can actually see what your script was doing right before it failed.
Use trap for robust clean-ups
A trap is a snippet of code that the shell executes when it exits or receives a signal. For example, pressing CTRL-C in the terminal where the script is running generates the INT signal. killing the process by default generates a TERM (I.e., terminate) signal.
I find traps most useful for making sure my scripts clean-up after themselves whatever happens (e.g., a non-zero error code in -e mode).
For example:
#!/bin/sh -e
TMPFILE=$(tempfile)
trap 'echo "removing $TMPFILE"; rm -f $TMPFILE' INT TERM EXIT
echo TMPFILE=$TMPFILE
echo hello world > $TMPFILE
cat $TMPFILE
# gives user a chance to press CTRL-C
sleep 3
# false always returns an error
false
echo "NEVER REACHED"
Note that you can only set one trap per signal. If you set a new trap you're implicitly disabling the old one. You can also disable a trap by specifying - as the argument, like this:
trap - INT TERM EXIT
Turn on -e mode (do you feel lucky - punk?)
In this mode any command your script runs which returns a non-zero exitcode - an error in the world of shell - will cause your script to itself terminate immediately with an error.
You can do that in your shebang line:
#!/bin/sh -e
Or using set:
set -e
Yes, this is what you want. A neat predictable failure is infinitely better than a noisy unreliable failure.
If you REALLY want to ignore an error, be explicit about it:
# I don't care if evil-broken-command fails
evil-broken-command || true
Oh and as long as you're messing with shell modes, -e goes well with -x (which I like to think of as shell X-ray).
Like this:
#!/bin/sh -ex
Or like this:
# turn -x on if DEBUG is set to a non-empty string
[ -n "$DEBUG" ] && set -x
That way you can actually see what your script was doing right before it failed.
Use trap for robust clean-ups
A trap is a snippet of code that the shell executes when it exits or receives a signal. For example, pressing CTRL-C in the terminal where the script is running generates the INT signal. killing the process by default generates a TERM (I.e., terminate) signal.
I find traps most useful for making sure my scripts clean-up after themselves whatever happens (e.g., a non-zero error code in -e mode).
For example:
#!/bin/sh -e
TMPFILE=$(tempfile)
trap 'echo "removing $TMPFILE"; rm -f $TMPFILE' INT TERM EXIT
echo TMPFILE=$TMPFILE
echo hello world > $TMPFILE
cat $TMPFILE
# gives user a chance to press CTRL-C
sleep 3
# false always returns an error
false
echo "NEVER REACHED"
Note that you can only set one trap per signal. If you set a new trap you're implicitly disabling the old one. You can also disable a trap by specifying - as the argument, like this:
trap - INT TERM EXIT
# if you're using bash, you can use $BASHPID in place of $$
ReplyDeletefor sig in INT TERM EXIT; do
trap "rm -f \"\$TMPFILE\"; [[ $sig == EXIT ]] || kill -$sig $$" $sig
done