2

I'm using Bash specifically, but I imagine most shells behave the same way.

I often do

hg pull && hg update --clean

But it just occurred to me, if hg pull returns 0 on success, why does it execute the hg up command?

Typically the way the && operator works is that it will only execute the next argument if the previous one was truthy. Is 0 truthy in Bash, or what?

This came up because I tried doing the equivalent in Python, but I had to write it like this:

call(['hg','pull']) or call(['hg','update','--clean'])
5
  • 1
    0 is considered the successful return code because there is generally only one way for a command to succeed, but many ways ( corresponding to many available non-zero values) for a command to fail. In shell, it helps to think not in terms of "true" or "false", but in terms of "success" or "failure".
    – chepner
    Commented May 3, 2013 at 18:07
  • Duplicate of this previous question Commented May 3, 2013 at 18:29
  • @chepner: I understand why 0 is considered successful, I just don't understand how && "knows" that if it can also be used in a programming context where 0 is false, and everything else is true.
    – mpen
    Commented May 3, 2013 at 18:36
  • @GordonDavisson: You're right. Please vote to close :-) Didn't see that one.
    – mpen
    Commented May 3, 2013 at 18:38
  • 1
    @Mark: Like I said, don't think in terms of true and false. a && b simply means "run a, and if it succeeds (i.e., exit code of 0), run b. &&, despite its appearance, is not a Boolean operator in shell script.
    – chepner
    Commented May 3, 2013 at 18:51

2 Answers 2

3

As help, you can remember than command when end correctly (without errors) mean true. And his exit codes are zero

so,

  • correct = OK = true = exit status 0
  • incorrect = BAD = false = exit status > 0

therefore, for example, the correct way deleting all files recursively is

$ pwd
/
$ cd /tnp && rm -rf *
cd: can't change directory  #and the rm WILL NOT executed

and not

$ pwd
/
$ cd /tnp ; rm -rf *
cd: can't change directory  #but the rm IS executed (in the root directory)

Added:

command1 && command2 && command3
               ^            ^
               |            +-- run, only when command2 exited OK (zero)
               |
               +--run only when command1 exited OK (zero) 

So, command3 will not executed if command1 or command2 failed. (when comman1 fails, the command2 will don't execued (fail) so command3 not executed too.

Play with the next

run() {
    echo "comand-$2($1)"
    return $1
}

ok() {
    run 0 $1
}
fail() {
    run 1 $1
}

echo "OK && OK && ANY"
ok A && ok B 0 && ok C
echo

echo "OK && FAIL && ANY"
ok A 0 && fail B 1 && ok C
echo

echo "FAIL && ANY && ANY"
fail A && ok B && ok C
echo

echo "OK || ANY || ANY"
ok A || ok B || ok C
echo

echo "FAIL || OK || ANY"
fail A || ok B || ok C
echo

echo "FAIL || FAIL || OK"
fail A || fail B || ok C
echo

echo "FAIL && OK || OK"
fail A && ok B || ok C
echo

the result

OK && OK && ANY
comand-A(0)
comand-B(0)
comand-C(0)

OK && FAIL && ANY
comand-A(0)
comand-B(1)

FAIL && ANY && ANY
comand-A(1)

OK || ANY || ANY
comand-A(0)

FAIL || OK || ANY
comand-A(1)
comand-B(0)

FAIL || FAIL || OK
comand-A(1)
comand-B(1)
comand-C(0)

FAIL && OK || OK
comand-A(1)
comand-C(0)

The last construction is neat, because you can write

command1 && (commands if the command1 is successful) || (commands if not)
2
  • Like I wrote in the question, I know it returns 0 on success, but how does chaining with && work?
    – mpen
    Commented May 3, 2013 at 18:08
  • Thanks for the update. I think my misunderstanding was that I knew 0 was a successful return code, but I figured && would still treat it as a falsy value because of how it's used in every other language.
    – mpen
    Commented May 3, 2013 at 18:42
2

The '&&' operator operates in two ways in Bash.

On one hand, it's a conditional operator as you'd expect:

if [ $condition1 ] && [ $condition2 ]
#  Same as:  if [ $condition1 -a $condition2 ]
#  Returns true if both condition1 and condition2 hold true...

if [[ $condition1 && $condition2 ]]    # Also works.
#  Note that && operator not permitted inside brackets
#+ of [ ... ] construct.

On the other hand, it can be used to concatenate commands, in which case it explicitly checks the return codes of the commands and proceeds down the chain when the value is 0. See the doc.

3
  • Your first example with [ ... ] is also an example of && being used to chain two commands. It is not, strictly speaking, the same as [ ... -a ...], which is a single command with multiple arguments, rather than two separate commands of which the second is execute if the first succeeds.
    – chepner
    Commented May 3, 2013 at 18:06
  • When you say it operates in two ways, do you mean that in the context of [ ... ] it evaluates 0 as falsy and everything else as truthy?
    – mpen
    Commented May 3, 2013 at 18:40
  • to clarify, what chepner said is exactly correct. the construct [ ... ] has a return code: 0 on success (or "truthy") and 1 on failure (or "falsy"). for example, running [ -d foo ] by itself and then doing an echo $? to see its return code will show you that 0 is returned when the directory exists and 1 is returned when it does not. so the entire [ ... ] is treated the same way as a command. Commented May 3, 2013 at 18:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.