<> == Why doesn't set -e (or set -o errexit, or trap ERR) 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. This does not work well in practice. The goal of automatic error detection is a noble one, but it requires the ability to ''tell'' when an error actually occurred. In modern high-level languages, most tasks are performed by using the language's builtin commands or features. The language knows whether (for example) you tried to divide by zero, or open a file that you can't open, and so on. It can take action based on this knowledge. But in the shell, most of the tasks you actually care about are done by ''external'' programs. The shell can't tell whether an external program encountered something that it considers an error -- and even if it could, it wouldn't know whether the error is an ''important'' one, worthy of aborting the entire program, or whether it should carry on. The only information conveyed to the shell by the external program is an exit status -- by convention, 0 for success, and non-zero for "some kind of error". The developers of the original Bourne shell decided that they would create a feature that would allow the shell to check the exit status of every command that it runs, and abort if one of them returns non-zero. Thus, `set -e` was born. But many commands return non-zero even when there ''wasn't'' an error. For example, {{{ if [ -d /foo ]; then ...; else ...; fi }}} If the directory doesn't exist, the `[` command returns non-zero. Clearly we don't want to abort when that happens -- our script wants to handle that in the `else` part. So the shell implementors made a bunch of special rules, like "commands that are part of an `if` test are immune", and "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. 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. [[https://www.in-ulm.de/~mascheck/various/set-e/|A reference comparing behavior across various historical shells]] also exists. === Story time === Consider this allegory, originally posted to [[https://lists.gnu.org/archive/html/bug-bash/2017-03/msg00171.html|bug-bash]]: {{{#!highlight text numbers=off Once upon a time, a man with a dirty lab coat and long, uncombed hair showed up at the town police station, demanding to see the chief of police. "I've done it!" he exclaimed. "I've built the perfect criminal-catching robot!" The police chief was skeptical, but decided that it might be worth the time to see what the man had invented. Also, he secretly thought, it might be a somewhat unwise move to completely alienate the mad scientist and his army of hunter robots. So, the man explained to the police chief how his invention could tell the difference between a criminal and law-abiding citizen using a series of heuristics. "It's especially good at spotting recently escaped prisoners!" he said. "Guaranteed non-lethal restraints!" Frowning and increasingly skeptical, the police chief nevertheless allowed the man to demonstrate one robot for a week. They decided that the robot should patrol around the jail. Sure enough, there was a jailbreak a few days later, and an inmate digging up through the ground outside of the prison facility was grabbed by the robot and carried back inside the prison. The surprised police chief allowed the robot to patrol a wider area. The next day, the chief received an angry call from the zookeeper. It seems the robot had cut through the bars of one of the animal cages, grabbed the animal, and delivered it to the prison. The chief confronted the robot's inventor, who asked what animal it was. "A zebra," replied the police chief. The man slapped his head and exclaimed, "Curses! It was fooled by the black and white stripes! I shall have to recalibrate!" And so the man set about rewriting the robot's code. Black and white stripes would indicate an escaped inmate UNLESS the inmate had more than two legs. Then it should be left alone. The robot was redeployed with the updated code, and seemed to be operating well enough for a few days. Then on Saturday, a mob of children in soccer clothing, followed by their parents, descended on the police station. After the chaos subsided, the chief was told that the robot had absconded with the referee right in the middle of a soccer game. Scowling, the chief reported this to the scientist, who performed a second calibration. Black and white stripes would indicate an escaped inmate UNLESS the inmate had more than two legs OR had a whistle on a necklace. Despite the second calibration, the police chief declared that the robot would no longer be allowed to operate in his town. However, the news of the robot had spread, and requests from many larger cities were pouring in. The inventor made dozens more robots, and shipped them off to eager police stations around the nation. Every time a robot grabbed something that wasn't an escaped inmate, the scientist was consulted, and the robot was recalibrated. Unfortunately, the inventor was just one man, and he didn't have the time or the resources to recalibrate EVERY robot whenever one of them went awry. The robot in Shangri-La was recalibrated not to grab a grave-digger working on a cold winter night while wearing a ski mask, and the robot in Xanadu was recalibrated not to capture a black and white television set that showed a movie about a prison break, and so on. But the robot in Xanadu would still grab grave-diggers with ski masks (which it turns out was not common due to Xanadu's warmer climate), and the robot in Shangri-La was still a menace to old televisions (of which there were very few, the people of Shangri-La being on the average more wealthy than those of Xanadu). So, after a few years, there were different revisions of the criminal-catching robot in most of the major cities. In some places, a clever criminal could avoid capture by wearing a whistle on a string around the neck. In others, one would be well-advised not to wear orange clothing in certain rural areas, no matter how close to the Harvest Festival it was, unless one also wore the traditional black triangular eye-paint of the Pumpkin King. Many people thought, "This is lunacy!" But others thought the robots did more good than harm, all things considered, and so in some places the robots are used, while in other places they are shunned. The end.}}} === Exercises === Or, "so you think `set -e` is OK, huh?" ''Exercise 1: why doesn't this example print anything?'' {{{#!highlight bash #!/usr/bin/env bash set -e i=0 let i++ echo "i is $i" }}} ''Exercise 2: why does '''this''' one sometimes appear to work? In which versions of bash does it work, and in which versions does it fail?'' {{{#!highlight bash #!/usr/bin/env bash set -e i=0 ((i++)) echo "i is $i" }}} ''Exercise 3: why aren't these two scripts identical?'' {{{#!highlight bash #!/usr/bin/env bash set -e test -d nosuchdir && echo no dir echo survived }}} {{{#!highlight bash #!/usr/bin/env bash set -e f() { test -d nosuchdir && echo no dir; } f echo survived }}} ''Exercise 4: why aren't '''these''' two scripts identical?'' {{{#!highlight bash set -e f() { test -d nosuchdir && echo no dir; } f echo survived }}} {{{#!highlight bash set -e f() { if test -d nosuchdir; then echo no dir; fi; } f echo survived }}} ''Exercise 5: under what conditions will this fail?'' {{{#!highlight bash set -e read -r foo < configfile }}} ([[BashFAQ/105/Answers|Answers]]) === But wait, there's more! === Even if you use `expr(1)` (which we ''do not'' recommend -- use [[ArithmeticExpression|arithmetic expressions]] instead), you still run into the same problem: {{{#!highlight bash set -e foo=$(expr 1 - 1) # The following command will not be executed: echo survived }}} Subshells from command substitution unset `set -e`, however (unless `inherit_errexit` is set with Bash 4.4): {{{#!highlight bash set -e foo=$(expr 1 - 1; true) # Will run: echo survived }}} Note that set -e is '''not''' unset for commands that are run asynchronously, for example with process substitution: {{{#!highlight bash set -e mapfile foo < <(true; echo foo) echo ${foo[-1]} # foo mapfile foo < <(false; echo foo) echo ${foo[-1]} # bash: foo: bad array subscript }}} Another pitfall associated with `set -e` occurs when you use commands that ''look like'' assignments but aren't, such as `export`, `declare`, `typeset` or `local`. {{{#!highlight bash set -e f() { local var=$(somecommand that fails); } f # will not exit g() { local var; var=$(somecommand that fails); } g # will exit }}} In function `f`, the exit status of `somecommand` is discarded. It won't trigger the `set -e` because the exit status of `local` masks it (the assignment to the variable succeeds, so `local` returns status 0). In function `g`, the `set -e` is triggered because it uses a ''real'' assignment which returns the exit status of `somecommand`. A particularly dangerous pitfall with `set -e` is combining functions with conditionals. The following snippets will not behave the same way: {{{#!highlight bash set -e f() { false; echo "This won't run, right?"; } f echo survived }}} {{{#!highlight bash set -e f() { false; echo "This won't run, right?"; } if f; then # same with: f && echo survived echo survived fi }}} As soon as a function is used as a conditional (in a [[BashGuide/TestsAndConditionals#Control_Operators_.28.26.26_and_.7C.7C.29|list]] or with a [[BashGuide/TestsAndConditionals#Conditional_Blocks_.28if.2C_test_and_.5B.5B.29|conditional]] test or loop) `set -e` stops being applied within the function. This may not only cause code to unexpectedly start executing in the function but also change its return status! Using [[ProcessSubstitution|Process substitution]], the exit code is also discarded as it is not visible from the main script: {{{#!highlight bash set -e cat <(somecommand that fails) echo survived }}} Using a pipe makes no difference, as only the ''rightmost'' process is considered: {{{#!highlight bash set -e somecommand that fails | cat - echo survived }}} `set -o pipefail` is a workaround by returning the exit code of the ''first'' failed process: {{{#!highlight bash set -e -o pipefail failcmd1 | failcmd2 | cat - # The following command will not be executed: echo survived }}} though with pipefail in effect, code like this will sometimes cause an error, depending on whether the output of somecmd exceeds the size of the pipe buffer or not: {{{#!highlight bash set -e -o pipefail somecmd | head -n1 # The following command will sometimes be executed, depending on how much output somecmd writes: echo survived }}} === So-called strict mode === In the mid 2010s, some people decided that the combination of `set -e`, `set -u` and `set -o pipefail` should be used ''by default'' in all new shell scripts. They call this [[http://redsymbol.net/articles/unofficial-bash-strict-mode/|unofficial bash strict mode]], and they claim that it "makes many classes of subtle bugs impossible" and that if you follow this policy, you will "spend much less time debugging, and also avoid having unexpected complications in production". As we've already seen in the exercises above, these claims are dubious at best. The behavior of `set -e` is quite unpredictable. If you choose to use it, you will have to be hyper-aware of all the false positives that can cause it to trigger, and work around them by "marking" every line that's ''allowed'' to fail with something like `||true`. === Conclusions === GreyCat's personal recommendation is simple: don't use `set -e`. Add your own error checking instead. rking's personal recommendation is to go ahead and use `set -e`, but beware of possible gotchas. It has useful semantics, so to exclude it from the toolbox is to give into FUD. geirha's personal recommendation is to handle errors properly and not rely on the unreliable `set -e`.