72

The set -e command makes a bash script fail immediately when any command returns an non-zero exit code.

  1. Is there an easy and elegant way to disable this behaviour for an individual command within a script?

  2. At which places is this functionality documented in the Bash Reference Manual (http://www.gnu.org/software/bash/manual/bashref.html)?

7 Answers 7

62
  1. Something like this:

    #!/usr/bin/env bash
    
    set -e
    echo hi
    
    # disable exitting on error temporarily
    set +e
    aoeuidhtn
    echo next line
    
    # bring it back
    set -e
    ao
    
    echo next line
    

    Run:

    $ ./test.sh
    hi
    ./test.sh: line 7: aoeuidhtn: command not found
    next line
    ./test.sh: line 11: ao: command not found
    
  2. It's described in set builtin help:

    $ type set
    set is a shell builtin
    $ help set
    (...)
    Using + rather than - causes these flags to be turned off.
    

The same is documented here: https://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin.

2
  • Thanks! I found another possibility: #!/bin/bash set -e # Uncomment next line to see set -e effect: #blubb if blubb; then echo "Command blubb was succesful." else echo "Command blubb failed. Exit code: $?" fi echo normal script exit
    – Gustave
    Jul 14, 2015 at 16:20
  • This has a side-effect though. If the whole script doesn't have set -e on it (e.g. depends on execution context, like a command line param to bash, decided by the user), then this will force-fully, always, in every context, enable set -e after the "bad" command.
    – TWiStErRob
    Aug 28, 2022 at 21:14
38

An alternative to unsetting the bail on error would be to force a success no matter what. You can do something like this:

cmd_to_run || true

That will return 0 (true), so the set -e shouldn't be triggered

6
  • 6
    faulty_cmd || : is another popular idiom for this. (The : is a command synonymous to true).
    – ulidtko
    Aug 26, 2016 at 12:24
  • 5
    @ulidtko - interesting. Led me down the rabbit hole to find this stackoverflow.com/questions/3224878/… to get the history on true vs. :
    – ernie
    Aug 26, 2016 at 17:55
  • 1
    Really nice solution with a caveat: if you need the exit code, you can't use this.
    – TWiStErRob
    Aug 28, 2022 at 21:15
  • @TWiStErRob Can't one use parentheses for getting the exit code? Smth like (CODE=cmd_to_run) || true
    – Dr_Zaszuś
    Jan 11, 2023 at 17:12
  • 1
    Please don't use :. You're saving 3 characters but making it much harder to understand.
    – Timmmm
    Jun 12, 2023 at 8:21
31

If you are trying to catch the return/error code (function or fork), this works:

function xyz {
    return 2
}

xyz && RC=$? || RC=$?
1
  • 2
    Oh this one is clever. Couldn't find a way to use set -e and still grab the rc. Closest I came was putting the cmd in an if statement... which i guess this is. Much shorter though! Thanks! Aug 12, 2020 at 21:32
15

If the the "exit immediately shell option" applies or is ignored depends on the context of the executed command (see Bash Reference Manual section on the Set Builtin - thanks to Arkadiusz Drabczyk).

Especially, the option is ignored if a command is part of the test in an if statement. Therefore it is possible to execute a command and check for its success or failure within an "exit immediately context" using an if statement like this:

#!/bin/bash

set -e

# Uncomment next line to see set -e effect:
#blubb

if blubb; then
  echo "Command blubb was succesful."
else 
  echo "Command blubb failed. Exit code: $?"
fi
echo "Script exited normally."

It is possible to omit the "then" statement and use fewer lines:

if blubb; then :;
else echo "Command blubb failed. Exit code: $?"; fi
1
  • Another, even shorter, way to write the short form is: blubb || echo "Command blubb failed. Exit code: $?"
    – j3h
    Jan 7, 2022 at 17:59
9

Another approach, which I find fairly straightforward (and applies to other set options in addition to -e):

Make use of $- to restore settings.

For example:

oldopt=$-
set +e
# now '-e' is definitely disabled.
# do some stuff...

# Restore things back to how they were
set -$oldopt

Though for -e specifically, the options others have mentined (|| true or "put inside an if") may be more idiomatic.

1

I like to start subshell if want to change something temporarily. Below command demonstrates that first bad_command is ignored and second aborts execution.

bash -c 'set -e ;\
( set +e; bad_command ; echo still here ) ;\
echo even here ; \
bad_command ; \
echo but not here;'
1
  • 1
    This is a really clean way of doing it, no side effects like (+e/-e pair on the same level), no operator hacking (&&/||), just a nice sub-shell with local state. 👏
    – TWiStErRob
    Aug 28, 2022 at 21:12
0

I actually had a similar question recently (though I didn't post, I got around to it), and, from what I can see, it seems like just using set +e before the command and set -e afterward works most elegantly. Here's an example, grabbing the response of the command and not letting the error throw it away.

#!/bin/sh

args=""
for argcol in $*
do
    args="${args} ${argcol}"
done
fortunevar=""
fortfail=""
{
    set +e
    fortunevar=`fortune $args`
    fortfail=$?
    set -e
} &> /dev/null
if [ $fortfail == 0 ]
then
    echo ${fortunevar}
    say ${fortunevar}
else
    echo misfortune: an illegal option was detected!
    echo misfortune: usage: misfortune [-afilosw] [-m pattern][ [#%] file/directory/all]
fi

This grabs the output of 'fortune', checking its exit status, and echoes and says it. I think this is what you were asking for, or at least something similar? Anyway, hope this helps.

1
  • To clarify, the bit in curly parentheticals being redirected to /dev/null is what you're after, I believe. It makes the command have no ouput and checks its error status without exiting the program. Jul 14, 2015 at 15:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .