Why Does Bash Not Stop on Error for Failures in Sequence of Short-Circuited Commands

Why does bash not stop on error for failures in sequence of short-circuited commands?

The logic here is that your use of && already is error-checking. The same way bash doesn't treat a failure within an if condition as worth aborting, even with set -e.

When you wrap the commands in a parenthesis, you are actually running those commands within a subshell, so the script itself only sees the return of that subshell, ie: it isn't aware that && is involved at all, so it aborts as you expect.

Stop on first error

Maybe you want set -e:

www.davidpashley.com/articles/writing-robust-shell-scripts.html#id2382181:

This tells bash that it should exit the script if any statement returns a non-true return value. The benefit of using -e is that it prevents errors snowballing into serious issues when they could have been caught earlier. Again, for readability you may want to use set -o errexit.

Ignoring specific errors in a shell script

In order to cause bash to ignore errors for specific commands you can say:

some-arbitrary-command || true

This would make the script continue. For example, if you have the following script:

$ cat foo
set -e
echo 1
some-arbitrary-command || true
echo 2

Executing it would return:

$ bash foo
1
z: line 3: some-arbitrary-command: command not found
2

In the absence of || true in the command line, it'd have produced:

$ bash foo
1
z: line 3: some-arbitrary-command: command not found

Quote from the manual:

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 !. A trap on ERR, if set, is executed before the shell
exits.

EDIT: In order to change the behaviour such that in the execution should continue only if executing some-arbitrary-command returned file not found as part of the error, you can say:

[[ $(some-arbitrary-command 2>&1) =~ "file not found" ]]

As an example, execute the following (no file named MissingFile.txt exists):

$ cat foo 
#!/bin/bash
set -u
set -e
foo() {
rm MissingFile.txt
}
echo 1
[[ $(foo 2>&1) =~ "No such file" ]]
echo 2
$(foo)
echo 3

This produces the following output:

$ bash foo 
1
2
rm: cannot remove `MissingFile.txt': No such file or directory

Note that echo 2 was executed but echo 3 wasn't.

Understanding Bash short-circuiting

This is actually a very common Bash pitfall. It is not a bug.

returnNumber 0 evaluates to true, so the second block (joined by logical and &&) is evaluated as well to make sure the result of first && second is still true.

The second block outputs OK but evaluates to false, so now the result of first && second is false. This means that the third portion (joined by logical or ||) must be evaluated as well, causing NG to be displayed as well.


Instead of relying on && and ||, you should be using if statements:

if returnNumber 0; then
echo 'OK'
returnNumber 1
else
echo 'NG'
fi

tl;dr: Never use x && y || z when y can return a non-zero exit status.

In a Bash script, how can I exit the entire script if a certain condition occurs?

Try this statement:

exit 1

Replace 1 with appropriate error codes. See also Exit Codes With Special Meanings.

set -e and short tests

Regarding my problem of the script unexpectedly dying, it is due to running those short tests in a subshell, or from a function, and said function returns a value different than zero:

Original example:

$ set -e
$ false && true
$ echo $?
1

Using a subshell or function:

$ set -e
$ (false && true)
<script died>

$ set -e
$ variable=$(false && true)
<script died>

$ set -e
$ foo()(false && true)
$ foo
<script died>

$ set -e
$ foo(){ false && true; }
$ foo
<script died>

Possible solutions:

$ set -e
$ (false && true ||:)
$ (false && true) ||:
$ (if false; then true; fi)


Related Topics



Leave a reply



Submit