dimba
dimba

Reputation: 27631

When to use set -e

I come across set -e a some time ago and I admit I love it. Now, after some time I'm back to write some bash scripting.

My question is if there are some best practices when to use set -e and when not to use it (.e.g. in small/big scripts etc.) or should I rather use a pattern like cmd || exit 1 to track errors?

Upvotes: 34

Views: 24571

Answers (4)

andrewdotn
andrewdotn

Reputation: 34873

Yes, you should always use it. People make fun of Visual Basic all the time, saying it's not a real programming language, partly because of its “On Error Resume Next” statement. Yet that is the default in shell! set -e should have been the default. The potential for disaster is just too high.


In places where it's ok for a command to fail, you can use || true or its shortened form ||:, e.g.

grep Warning build.log ||:

In fact you should go a step further, and have

set -eu
set -o pipefail

at the top of every bash script.

-u makes it an error to reference a non-existent environment variable such as ${HSOTNAME}, at the cost of requiring some gymnastics with checking ${#} before you reference ${1}, ${2}, and so on.

pipefail makes things like misspeled-command | sed -e 's/^WARNING: //' raise errors.

Upvotes: 80

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 70977

You love it!?

For my self, I prefer in a wide, having in my .bashrc a line like this:

trap '/usr/games/fortune /usr/share/games/fortunes/bofh-excuses' ERR

( on debian: apt-get install fortunes-bofh-excuses :-)

But it's only my preference ;-)

More seriously

lastErr() {
    local RC=$?
    history 1 |
         sed '
  s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/;
  s/$/", rc: '"$RC/"
}
trap "lastErr" ERR

Gna
bash: Gna : command not found
cmd: "Gna", args: "", rc: 127

Gna gna
cmd: "Gna", args: "gna", rc: 127

"Gna gna" foo
cmd: "Gna gna", args: "foo", rc: 127

Well, from there, you could:

trap "lastErr >>/tmp/myerrors" ERR
"Gna gna" foo

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1

Or better:

lastErr() {
local RC=$?
history 1 |
     sed '
  s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/;
  s/$/", rc: '"$RC/
  s/^/$(date +"%a %d %b %T ")/"
}
"Gna gna" foo

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1
Tue 20 Nov 18:29:18 cmd: "Gna gna", args: "foo", rc: 127

... You could even add other informations like $$, $PPID, $PWD or maybe your..

Upvotes: 4

Greg A. Woods
Greg A. Woods

Reputation: 2792

If your script code checks for errors carefully and properly where necessary, and handles them in an appropriate manner, then you probably don't ever need or want to use set -e.

On the other hand if your script is a simple sequential list of commands to be run one after another, and if you want the script to terminate if any one of those fail, then sticking set -e at the top would be exactly what you would want to do to keep your script simple and uncluttered. A perfect example of this would be if you're creating a script to compile a set of sources and you want the compile to stop after the first file with errors is encountered.

More complex scripts can combine these methods since you can use set +e to turn its effect back off again and go back to explicit error checking.

Note that although set -e is supposed to cause the shell to exit IFF any untested command fails, it is wise to turn it off again when your code is doing its own error handling as there can easily be weird cases where a command will return a non-zero exit status that you're not expecting, and possibly even such cases that you might not catch in testing, and where sudden fatal termination of your script would leave something in a bad state. So, don't use set -e, or leave it turned on after using it briefly, unless you really know that you want it.

Note also that you can still define an error handler with trap ERR to do something on an error condition when set -e is in effect, as that will still be run before the shell exits.

Upvotes: 6

Saddam Abu Ghaida
Saddam Abu Ghaida

Reputation: 6749

When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.

Upvotes: 0

Related Questions