Reputation: 669
How can I run a function as a "tested command" and perform action on failure, while still aborting the function as soon as an error occur? Consider the following script
#!/bin/bash -e
function foo() {
echo Entering foo
false
echo Should not reach this line
}
foo || echo I want to see this line on failure in foo
foo
The output I'm getting is
Entering foo
Should not reach this line
Entering foo
While I would like to get
Entering foo
I want to see this line on failure in foo
Entering foo
I guess what I'm looking for is a way to mark the function as untested command. According bash man page
-e errexit
Exit immediately if any untested command fails in non-interactive
mode. The exit status of a command is considered to be explicitly
tested if the command is part of the list used to control an if,
elif, while, or until; if the command is the left hand operand of
an “&&” or “||” operator; or if the command is a pipeline preceded
by the ! operator. If a shell function is executed and its exit
status is explicitly tested, all commands of the function are con‐
sidered to be tested as well.
EDIT The expected output was wrong. edited it for clarity
Upvotes: 4
Views: 939
Reputation: 669
I ended up wrapping the code to do so in a utility function below.
#!/bin/bash -e
# Runs given code aborting on first error and taking desired action on failure
# $1 code to invoke, can be expression or function name
# $2 error handling code, can be function name or expressions
function saferun {
set +e
(set -E ; trap 'exit 1' ERR ; eval $1)
[ $? -ne 0 ] && eval $2
set -e
}
function foo() {
echo Entering foo
false
echo Should not reach this line
}
saferun foo "echo I want to see this line on failure in foo"
foo
Let's break it down:
set +e
and set -e
are used to suppress failure on error, as otherwise the script will just exit on first errortrap
is used to abort the execution on any error (instead of set -e
) ()
is used to run the given code in subshell, so outer script will keep running after failure, and set -E
is used to pass the trap into the subshell. so (set -E ; trap 'exit 1' ERR ; eval $1)
run the given code / function aborting on first error while not exiting the whole script $? -ne 0
check for failures and eval $2
runs the error handling codeUpvotes: 0
Reputation: 15613
set -e
is disabled in the first call of foo
since it's on the left hand side of ||
.
Also, you would never see the I want to see this ...
string being outputted, unless the last echo
in foo
somehow failed (it's that last echo
in foo
that determines the exit status of the function).
foo() {
echo Entering foo
false && echo Should not reach this line
}
foo || echo I want to see this line on failure in foo
foo
The above outputs (with or without set -x
)
Entering foo
I want to see this line on failure in foo
Entering foo
Now false
is the last executed statement in foo
.
Upvotes: 3