What is the difference between the following methods of chaining commands?
cmd1; cmd2
cmd1 && cmd2
What is the difference between the following methods of chaining commands?
cmd1; cmd2
cmd1 && cmd2
Assume there is command1 && command2
.
In this case command2
will be executed if and only if command1
returned zero exit status.
;
is just a command separator. Thus command2
will be executed whatever command1
returned.
$> [[ "a" = "b" ]] && echo ok
$> [[ "a" = "b" ]]; echo ok
ok
cmd1; cmd2
executes cmd1
, then cmd2
, no matter what. It it exactly equivalent to putting cmd1
and cmd2
on separate lines¹. The return status of this compound command is that of cmd2
.
cmd1 && cmd2
executes cmd1
. Then, if the exit status of cmd1
is zero, cmd2
is executed. The return status of this compound command is that of cmd1
if it is nonzero (so cmd2
didn't run), and that of cmd2
otherwise (if it ran).
if cmd1; then cmd2; fi
is mostly equivalent to cmd1 && cmd2
. The main difference is that the version with if
returns 0 if cmd1
returns a nonzero status.
A command returns 0 to indicate success, and a nonzero error code (between 1 and 255, usually between 1 and 125 as higher values have other meanings) to indicate failure. Thus cmd1; cmd2
means “execute these commands in sequence no matter what”, whereas cmd1 && cmd2
means “execute these commands, but stop immediately if the first command fails”.
You can tell the shell to enter “exit on error” mode by running set -e
. In this mode, the shell exits as soon as any command returns a nonzero status, except in conditional constructs (the left side of &&
or ||
, the condition part of if
and while
). Thus, under set -e
, ;
(or a newline) is effectively equivalent to &&
².
¹ In terms on control flow, that is. Newlines and semicolons are executed in exactly the same way, but they aren't parsed in exactly the same way, so e.g. alias ls=true; ls
executes ls
and not true
because the parser performs alias resolution on ls
before it executes the alias definition.
¹ This isn't to say you can blindly replace &&
by ;
if you've added set -e
. For example, ;
has lower precedence than &&
, so cmd1 && cmd2 || cmd3
is equivalent to set -e; { cmd1; cmd2; } || cmd3
. Also, set -e
interacts badly with subshells — the details would derail this answer too far.
echo $(set -e; { false; (exit 2) } && true; echo $?)
vs echo $(set -e; { false && (exit 2) } && true; echo $?)
;)
– Stéphane Gimenez
Apr 23 '12 at 18:59
set -e
and subshells together can be funky.
– Gilles 'SO- stop being evil'
Apr 23 '12 at 20:02
cmd1; cmd2
is not necessarily the same as cmd1<newline>cmd2
. Cases that come to mind are when cmd1
affects the shell parsing like alias
commands or commands that change the localisation settings. For instance, alias foo=bar;foo
is not necessarily the same as alias foo=bar<newline>foo
.
– Stéphane Chazelas
Jan 11 '17 at 10:26
The first line will execute one command after another, irrespective of whether the first one succeeded or not. The second line is an example of shell logic: it will only execute the second command if the first one succeeded. This is because &&
is logical and
. Therefore, if the first command fails, the logical state of the entire line is known to be false and there is no need to evaluate the second command.
More practical, there's a difference between
cd /backup/old; rm -rf -- *
and
cd /backup/old && rm -rf -- *
. Besides it's a stupid way, the first one will remove your whole filesystem (or $HOME, depending how it's called) because it runs rm
regardless of whether the cd
succeeded or not.
command1 || command2
can be used to runcommand2
only ifcommand1
returned a nonzero exit status. – hammar Apr 22 '12 at 20:19&&
should not even look at the second argument when the first is already 0 - the result will be 0 anyway. – Konerak Apr 24 '12 at 08:570
indicates success, ortrue
while anything else isfalse
, so it's sort of reversed compared to "ordinary" booleans. – hammar Apr 27 '12 at 20:25