In Wick, everything by default runs in strict mode. This, among other things, sets -e
which tells Bash to exit if a command that was ran exited with an error code. It will also exit with the same error code as the command that failed.
set -e
errorCommand() {
false
}
echo "Will see this"
errorCommand()
echo "Will not see this"
This will exit with the code 1. We sometimes use exit codes to our advantage.
set -e
errorCommand() {
false
}
if ! errorCommand; then
echo "It returned a non-zero code, signalling false or failure."
# continue to do other stuff.
fi
Most of the time this works fine, but there are situations that might cause confusion. What the if
does in this case is disables the -e
flag which is why it doesn't just exit at that line.
Here are a few examples that might cause confusion.
set -e
errorCommand() {
false
echo "shouldn't get here right?"
}
if ! errorCommand; then
echo "Expected failure"
else
echo "Didn't expect a success"
fi
This outputs
shouldn't get here right?
Didn't expect a success
Two things to note:
- Without
-e
errorCommand
continues even though the first command exited with a non-zero value (false
). errorCommand
exits with 0 because theecho
command successfully completed.
Another thing to note is that there is no way to reenabled -e
once inside of one of these contexts. This applies even if you are running a subshell.
set -e
if ! (set -e; false; echo "it's fine"); then
echo "Expected failure"
else
echo "Didn't expect a success"
fi
Which outputs
Didn't expect a success
This can happen in many contexts. The description from a bug logged to fix the documentation says it can happen for any context including while
, until
, if
, elif
or !
. Below are some examples. Each of which will output "TEST".
set -e
! true; echo "TEST"
while false; true; do
echo "TEST"
# To prevent an infinite loop
exit 0
done
(set -e; false; true) && echo "TEST"
These are just simple examples. More complex situations make it harder to spot the problem.
The best option is to ensure all bash functions are written to operate in this special mode. You do that by capturing every possible command that could have stopped execution and force an end of the function.
# Old code
result=$(ls some-file)
list=( "some" "words" $(runSomething) )
grep -q "words" in-this-file
wickGetIfaceIp ipAddress tun0
# New code
result=$(ls some-file) || return $?
list=( "some" "words" $(runSomething) ) || return $?
grep -q "words" in-this-file || return $?
wickGetIfaceIp ipAddress tun0 || return $?
When you ensure that all functions are written this way, then they will always exit early regardless of strict mode and regardless of this error exit context. Consistent, predictable results from your code makes it far more reliable.