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 awhile
oruntil
keyword, part of
the test in anif
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 onERR
, 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
How to Set Environment Variable Within Gdb Using Shell Command
List Files That Are in Directory1 But Not in Directory2 and Vice Versa
How to Flush Stdout of a Running Process
Detecting If The Monitor Is Powered Off
How to Prevent Git from Committing Two Files with Names Differing Only in Case
How to Run Sh File from Another Sh File
Why Strace Shows Eagain (Resource Temporarily Unavailable)
Check What Conda Environment Is Currently Activated
Having Trouble Wrapping Functions in The Linux Kernel
Update .Bashrc from Provisioning Shell Script with Vagrant
Git Clone Gnutls Recv Error (-9): a Tls Packet with Unexpected Length Was Received
Linux Awk Comparing Two CSV Files and Creating a New File with a Flag
How to Use Sysfs Inside Kernel Module
How to Have Postgresql Not Collapse Punctuation and Spaces When Collating Using a Language
How to Make Fork Changes Reflect with The Master When Updated