Differences between revisions 2 and 3
Revision 2 as of 2010-08-18 16:25:07
Size: 1431
Editor: GreyCat
Comment: err, yeah, that blank line can't be there.
Revision 3 as of 2010-08-28 01:23:54
Size: 1733
Editor: GreyCat
Comment: examples! examples are fun!
Deletions are marked like this. Additions are marked like this.
Line 15: Line 15:
If the `[ -d /foo ]` command triggers the `set -e` abort when it returns non-zero (because the directory does not exist -- a case our script wants to handle in the `else` part), then obviously `set -e` isn't very useful. So the implementors decided to make a bunch of special rules, like "commands that are part of an `if` test are immune", or "commands in a pipeline, other than the last one, are immune". Clearly we don't want to abort when `[ -d /foo ]` returns non-zero (because the directory does not exist) -- our script wants to handle that in the `else` part. So the implementors decided to make a bunch of special rules, like "commands that are part of an `if` test are immune", or "commands in a pipeline, other than the last one, are immune".
Line 17: Line 17:
These rules are extremely convoluted. Worse, they ''change'' from one Bash version to another, as Bash attempts to track the extremely slippery POSIX definition of this "feature". When a SubShell is involved, it gets worse still. The behavior changes depending on whether Bash is invoked in POSIX mode. [[http://fvue.nl/wiki/Bash:_Error_handling|Another wiki]] has a page that covers this in more detail. Be sure to check the caveats. These rules are extremely convoluted, and they still fail to catch even some remarkably simple cases.
Line 19: Line 19:
GreyCat's personal recommendation is simple: don't use it. Add your own explicit error checking instead. ''Exercise for the reader: why doesn't this example print anything?''

{{{#!highlight bash
#!/bin/bash
set -e
i=0
let i++
echo "i is $i"
}}}

''Exercise 2: why does ''this'' one appear to work?''

{{{#!highlight bash
#!/bin/bash
set -e
i=0
((i++))
echo "i is $i"
}}}

Even worse, the rules ''change'' from one Bash version to another, as Bash attempts to track the extremely slippery POSIX definition of this "feature". When a SubShell is involved, it gets worse still -- the behavior changes depending on whether Bash is invoked in POSIX mode. [[http://fvue.nl/wiki/Bash:_Error_handling|Another wiki]] has a page that covers this in more detail. Be sure to check the caveats.

GreyCat's personal recommendation is simple: don't use `set -e`. Add your own error checking instead.

Why doesn't set -e (set -o errexit) do what I expected?

set -e was an attempt to add "automatic error detection" to the shell. Its goal was to cause the shell to abort any time an error occurred, so you don't have to put || exit 1 after each important command.

That goal is non-trivial, because many commands are supposed to return non-zero. For example,

  if [ -d /foo ]; then
    ...
  else
    ...
  fi

Clearly we don't want to abort when [ -d /foo ] returns non-zero (because the directory does not exist) -- our script wants to handle that in the else part. So the implementors decided to make a bunch of special rules, like "commands that are part of an if test are immune", or "commands in a pipeline, other than the last one, are immune".

These rules are extremely convoluted, and they still fail to catch even some remarkably simple cases.

Exercise for the reader: why doesn't this example print anything?

   1 #!/bin/bash
   2 set -e
   3 i=0
   4 let i++
   5 echo "i is $i"

Exercise 2: why does this one appear to work?

   1 #!/bin/bash
   2 set -e
   3 i=0
   4 ((i++))
   5 echo "i is $i"

Even worse, the rules change from one Bash version to another, as Bash attempts to track the extremely slippery POSIX definition of this "feature". When a SubShell is involved, it gets worse still -- the behavior changes depending on whether Bash is invoked in POSIX mode. Another wiki has a page that covers this in more detail. Be sure to check the caveats.

GreyCat's personal recommendation is simple: don't use set -e. Add your own error checking instead.

BashFAQ/105 (last edited 2021-03-11 06:07:25 by dsl-66-36-156-249)