Reputation: 9758
Have a look at this little script:
#!/bin/bash
function do_something() {(
set -e
mkdir "/opt/some_folder" # <== returns 1 -> abort?
echo "mkdir returned $?" # <== sets $0 to 0 again
rsync $( readlink -f "${BASH_SOURCE[0]}" ) /opt/some_folder/ # <== returns 23 -> abort?
echo "rsync returned $?" # <== sets $0 to 0 again
)}
# here every command inside `do_something` will be executed - regardless of errors
echo "run do_something in if-context.."
if ! do_something ; then
echo "running do_something did not work"
fi
# here `do_something` aborts on first error
echo "run do_something standalone.."
do_something
echo $?
I was trying to do what was suggested here (don't miss the extra parentheses introducing a sub-shell) but I didn't execute the function (do_something
in my case) separately but together with the if-expression.
Now when I run if ! do_something
the set -e
command seems to have no effect.
Can someone explain this to me?
Upvotes: 4
Views: 586
Reputation: 1820
Using a function to change settings and traps overcomes this limitation, at least in Homebrew Bash 5.2.15(1)
If I start with this:
errexit_ignore() {
set +e
trap - ERR
}
errexit_fail() {
set -e
trap failed ERR
}
errexit_fail
I can later do horrible useful things like this:
for customization in ${customizations[@]}; do
log_mark 2 "Checking ${customization} ..."
errexit_ignore
diff -qs "${expected}/${customization}" "${tmpdir}/${customization}"
diff_exitcode="${?}"
errexit_fail
if [ "${diff_exitcode}" != "0" ]; then ## If it's not the expected file, the customization may have already been applied
errexit_ignore
diff -qs "${tmpdir}/${customization}" "${customized}/${customization}"
diff_exitcode="${?}"
errexit_fail
if [ "${diff_exitcode}" != "0" ]; then ## If it's neither the expected file nor the customized file, either default has changed or the customization has changed
errexit_ignore
git ls-files --error-unmatch "${customized}/${customization}"
track_exitcode="${?}" ## Detect untracked file
git diff --exit-code "${customized}/${customization}"
diff_exitcode="${?}" ## Detect modified tracked file
errexit_fail
if [ "${track_exitcode}" != "0" -o "${diff_exitcode}" != "0" ]; then ## If the customization has uncomitted changes, assume the default hasn't changed
log_mark 1 "Customized ${customization} will be updated"
else ## If the default has changed, manual review is needed (which may result in an updated customization)
diff -u "${expected}/${customization}" "${tmpdir}/${customization}" || :
diff -u "${tmpdir}/${customization}" "${customized}/${customization}" || :
abort "Default version of ${customization} has changed, expected version must be updated and customization must be checked for compatibility"
fi
else
log_mark 1 "Customized ${customization} already in place"
fi
else
log_mark 1 "Default ${customization} has not changed"
fi
done
Notes:
trap function ERR
, set +e
doesn't work unless you also trap - ERR
errexit_ignore
nor errexit_fail
can be defined on a single line (I'm not sure why not)Upvotes: 0
Reputation: 2093
This is expected and described in the Bash Reference Manual.
-e
[...] The shell does not exit if the command that fails is part of the command list immediately following a
while
oruntil
keyword, part of the test in anif
statement, [...].[...]
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.
Upvotes: 8