i have a script that I accidentally ran without an underlying file present, and my script doesn't have a check for this file, because the script should stop when the command that requires that file exits 1
.
i got caught out because it went ahead and skipped the sleep
command and the ||exit 0
if
test that I have as some protection protection. i would really like to know why. the if test and exit works if the preceding command doesn't fail.
if i strip the script down I can see some unexpected behaviour where the script doesn't stop at the &&
and skips the next sleep
command.
is this not the correct way to use &&
?
you can test this here:
#!/bin/bash
mkdir /root/simulatecomplexcommandthatreturns1 &&
sleep 5m
echo "let's go ahead and delete all the stuff"
find /blah/ -delete
this is on debian 9
EDIT:
for clarity, I want the script to stop when it encounters an error and I have &&
. I just thought it was odd that it didn't run the sleep
command.
答案 0 :(得分:3)
The &&
only apply to next command, for a sequence, braces must be added:
#!/bin/bash
mkdir /root/simulatecomplexcommandthatreturns1 && {
sleep 5m
echo "let's go ahead and delete all the stuff"
find /blah/ -delete
}
or to avoid indent level the condition can be inverted
#!/bin/bash
mkdir /root/simulatecomplexcommandthatreturns1 || {
echo "something goes wrong"
exit 1
}
# ok, continue
sleep 5m
echo "let's go ahead and delete all the stuff"
find /blah/ -delete
答案 1 :(得分:1)
If you want a script to abort/exit as soon as a command pipeline exists with a non-zero status (that means the last command in the pipeline, unless pipefail
enabled), you might consider using:
set -e
In your example:
#!/bin/bash
set -e
mkdir /root/simulatecomplexcommandthatreturns1
sleep 5m
echo "let's go ahead and delete all the stuff"
find /blah/ -delete
when any of the commands fails, your script will exit.
Note however, this can sometimes lead to unwanted exits. For example it's normal for grep
to exit with error if no match was found (you might "silence" such commands with grep .. || true
ensuring the pipeline exits with success).
You'll probably be safer with manually testing for failure. For example:
if ! mkdir /root/simulatecomplexcommandthatreturns1; then
echo "Error description."
exit 1
fi
The usage of shortcircuiting &&
and ||
is best reserved for simple command sequences, when the execution of the next depends on successful exit of the previous. For example, the command pipeline:
mkdir /somedir && cp file /somedir && touch /somedir/file
will try to create a directory, if created successfully, it will try to copy the file; and if the file was copied successfully, it will touch the file.
Example with OR
:
cp file /somedir || exit 1
where we try to copy the file and we exit if copy failed.
But you should be very careful when combining the two, since the result can be unexpected. For example:
a && b || c
is not equal to:
if a; then b; else c; fi
because c
in the former expression will get executed whenever either of a
or b
fails (exits with a non-zero status). In the latter expression, c
is executed only if a
fails. For example:
true && false || echo "This also gets executed."
答案 2 :(得分:0)
You're using the wrong operator. What you need is ||
(example 2 below).
Explanation:
Note:
`A && B` # => Run `A`, and then `B` iff A ran successfully.
`A || B` # => Run `A`, and then `B` iff A did not run successfully.
答案 3 :(得分:0)
&&
is like the AND
operator with the property :
fail && <anything> equals fail
<anything> && fail equals fail
success && success equals success
So, if the first operand (or command) fails, there is no point in resolving the second command.
Hence,
when mkdir /root/simulatecomplexcommandthatreturns1
fails in
mkdir /root/simulatecomplexcommandthatreturns1 &&
sleep 5m
It skips the second command.
What you want here is ||
or the OR
operator
fail || fail equals fail
fail || success equals success
success || <anything> equals success
So, using if the mkdir /root/simulatecomplexcommandthatreturns1
fails in"
mkdir /root/simulatecomplexcommandthatreturns1 || sleep 5m
It will have to evaluate the second operand ie the sleep 5m
command.
EDIT :
Note that bash script do not exit if one of its command fails. It only exits when it reaches the end of the script or when exit
is called.
if you want to exit when a certain command fails, you would do something like :
$ theCommandThatCanFail || exit 1 # the first command returns fail and
# since its `OR` operator, the second
# command will be resolved