What does `set -o errtrace` do in a shell script?
From the manual:
errtrace
Same as -E.-E If set, any trap on ERR is inherited by shell functions,
command substitutions, and commands executed in a sub‐
shell environment. The ERR trap is normally not inher‐
ited in such cases.
When errtrace
is enabled, the ERR trap is also triggered when the error (a command returning a nonzero code) occurs inside a function or a subshell. Another way to put it is that the context of a function or a subshell does not inherit the ERR trap unless errtrace
is enabled.
#!/bin/bash
set -o errtrace
function x {
echo "X begins."
false
echo "X ends."
}
function y {
echo "Y begins."
false
echo "Y ends."
}
trap 'echo "ERR trap called in ${FUNCNAME-main context}."' ERR
x
y
false
true
Output:
X begins.
ERR trap called in x.
X ends.
Y begins.
ERR trap called in y.
Y ends.
ERR trap called in main context.
When errtrace
is not enabled:
X begins.
X ends.
Y begins.
Y ends.
ERR trap called in main context.
What does set -e mean in a bash script?
From help set
:
-e Exit immediately if a command exits with a non-zero status.
But it's considered bad practice by some (bash FAQ and irc freenode #bash FAQ authors). It's recommended to use:
trap 'do_something' ERR
to run do_something
function when errors occur.
See http://mywiki.wooledge.org/BashFAQ/105
How to trap ERR when using 'set -e' in Bash
chepner's answer is the best solution: If you want to combine set -e
(same as: set -o errexit
) with an ERR
trap, also use set -o errtrace
(same as: set -E
).
In short: use set -eE
in lieu of just set -e
:
#!/bin/bash
set -eE # same as: `set -o errexit -o errtrace`
trap 'echo BOO!' ERR
function func(){
ls /root/
}
# Thanks to -E / -o errtrace, this still triggers the trap,
# even though the failure occurs *inside the function*.
func
A more sophisticated example trap
example that prints the message in red and also prints the exit code:trap 'printf "\e[31m%s: %s\e[m\n" "BOO!" $?' ERR
man bash
says about set -o errtrace
/ set -E
:
If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.
What I believe is happening:
Without
-e
: Thels
command fails inside your function, and, due to being the last command in the function, the function reportsls
's nonzero exit code to the caller, your top-level script scope. In that scope, theERR
trap is in effect, and it is invoked (but note that execution will continue, unless you explicitly callexit
from the trap).With
-e
(but without-E
): Thels
command fails inside your function, and becauseset -e
is in effect, Bash instantly exits, directly from the function scope - and since there is noERR
trap in effect there (because it wasn't inherited from the parent scope), your trap is not called.
While the man
page is not incorrect, I agree that this behavior is not exactly obvious - you have to infer it.
Reuse bash error handling trap logic with set -e command cannot invoke error handling function correctly
@Charles Thanks for the suggestions. I will seriously consider about the pros and cons to improve my code.
For now, I found a stackoverflow link that is related to my issue. After I replaced "set -e" with "set -eE -o functrace", I get what I expect to achieve.
chepner's answer is the best solution: If you want to combine set -e
(same as: set -o errexit
) with an ERR
trap, also use set -o errtrace
(same as: set -E
).
In short: use set -eE
in lieu of just set -e
:
#!/bin/bash
set -eE # same as: `set -o errexit -o errtrace`
trap 'echo BOO!' ERR
function func(){
ls /root/
}
# Thanks to -E / -o errtrace, this still triggers the trap,
# even though the failure occurs *inside the function*.
func
man bash
says about set -o errtrace
/ set -E
:
If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.
What I believe is happening:
Without
-e
: Thels
command fails inside your function, and, due to being the last command in the function, the function reportsls
's nonzero exit code to the caller, your top-level script scope. In that scope, theERR
trap is in effect, and it is invoked (but note that execution will continue, unless you explicitly callexit
from the trap).With
-e
(but without-E
): Thels
command fails inside your function, and becauseset -e
is in effect, Bash instantly exits, directly from the function scope - and since there is noERR
trap in effect there (because it wasn't inherited from the parent scope), your trap is not called.
While the man
page is not incorrect, I agree that this behavior is not exactly obvious - you have to infer it.
How to use `set -e` inside a bash command substitution?
Q: How would you go about implementing this without
set -e
(i.e. print the output of a function only if something went wrong while executing it)?
You may use this way by checking return value of the function:
#!/usr/bin/env bash
foo() {
local n=$RANDOM
echo "Foo working with random=$n ..."
(($n % 2))
}
echo "Doing something that could fail..."
a="$(foo 2>&1)"
code=$?
if (($code == 0)); then
echo "Success!"
else
printf '{"ErrorCode": %d, "ErrorMessage": "%s"}\n' $code "$a"
exit $code
fi
Now run it as:
$> ./errScript.sh
Doing something that could fail...
Success!
$> ./errScript.sh
Doing something that could fail...
{"ErrorCode": 1, "ErrorMessage": "Foo working with random=27662 ..."}
$> ./errScript.sh
Doing something that could fail...
Success!
$> ./errScript.sh
Doing something that could fail...
{"ErrorCode": 1, "ErrorMessage": "Foo working with random=31864 ..."}
This dummy function code returns failure if $RANDOM
is even number and success for $RANDOM
being odd number.
Original answer for original question
You need to enable set -e
in command substitution as well:
#!/usr/bin/env bash
set -eu
set -o pipefail
foo() {
printf "Foo working... "
echo "Failed!"
false # point of interest #1
true # point of interest #2
}
printf "Doing something that could fail... "
a="$(set -e; foo)"
code=$?
if (($code == 0)); then
echo "Success!"
else
echo "Error:"
printf "${a}"
exit $code
fi
Then use it as:
./errScript.sh; echo $?
Doing something that could fail... 1
However do note that using set -e
is not ideal in shell scripts and it may fail to exit script in many scenarios.
Do check this important post on set -e
What does set -e and set -a do in bash.What are other options that i can use with set command
From the man page:
-a Mark variables and function which are modified or created for export to the environment of subsequent commands
-e Exit immediately if a pipeline (see Pipelines), which may consist of a single simple command (see Simple Commands), a list (see Lists),
or a compound command (see Compound Commands) returns a non-zero
status. The shell does not exit if the command that fails is part of
the command list immediately following a while or until keyword, part
of the test in an if statement, part of any command executed in a &&
or || list except the command following the final && or ||, any
command in a pipeline but the last, or if the command’s return status
is being inverted with !. If a compound command other than a subshell
returns a non-zero status because a command failed while -e was being
ignored, the shell does not exit. A trap on ERR, if set, is executed
before the shell exits.This option applies to the shell environment and each subshell
environment separately (see Command Execution Environment), and may
cause subshells to exit before executing all the commands in the
subshell.If a compound command or shell function executes in a context where -e
is being ignored, none of the commands executed within the compound
command or function body will be affected by the -e setting, even if
-e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where
-e is ignored, that setting will not have any effect until the compound command or the command containing the function call
completes.
using set -o in a bash script to set command line editing
Run . ~/bin/editorSet.sh
, not ~/bin/editorSet.sh
, to execute the script's commands inside the interactive shell you're already running. (In bash, but not all POSIX shells, you can use source
as a synonym for .
).
Otherwise, it runs in a new shell which exits when the script does, so the configuration changes do not last past the end of the script's execution.
Related Topics
How to Open Remotely Installed Sonar on a Browser
Linux How to Copy But Not Overwrite
Check If Directory Mounted with Bash
How to Create a Callback for "Monitor Plugged" on an Intel Graphics
How to Align the Columns of a Space Separated Table in Bash
Android Sdk on a 64-Bit Linux MAChine
Sort by Multiple Columns in Bash
How to Restart a Service If Its Dependent Service Is Restarted
Why Can Back-Quotes and $() for Command Substitution Result in Different Output
How to Confirm Sftp File Delivery
Find -Exec Cmd {} + VS | Xargs
What's an Alternative for Dtrace on Linux
How to Add .So File to the Java.Library.Path in Linux