Bash – How will bash evaluate following code

bashexit

This question has two parts:

(a) Understanding what the code snipped is doing

(b) Understanding the difference between exit status and return status in context of bash.

Here is the code snipped I am trying to understand:

if var=-2 && (( var+=2 ))
then
    echo "True"
else
    echo "False"
fi

Running this produces False. I cannot understand why this is happening.

If I understand this correctly here is what maybe happening with the if condition:

(a) var=-2 creates exit status of 0, because the assignment is a success

(b) (( var+=2 )) adds 2 to the value of var and the expression evaluates zero. So the exit status is 1 for this term

(c) 0 && 1 creates an exist status of 0 which is then used by if construct

The if construct is supposed to simply check the exit status and when it is zero it takes the then path. In step (c) above it is zero but the script still takes the else path. Is this correct way to understand this?

Also, I keep seeing various bash texts use exit status and return status interchangeably.

I doubt var=-2 assignment would have any kind of exit status because it is not a program. But any clarification on the difference between two will be great.

Best Answer

That's:

if
  first list of commands
then
  second list of commands
else
  third list of commands
fi

That's to run the second list of commands if the first list of commands returns with a true/success (zero) exit status, that is if the last run command in there returns with a zero exit status.

In:

var=-2 && ((var += 2))

It's cmd1 && cmd2 where cmd2 is only run if cmd1 is successful.

var=-2

Will typically be successful as long as $var has not been made read-only, so the ((var += 2)) command will be run:

((arithmetic expression))

Returns success/true as long as the expression is correctly evaluated (no syntax error) and the result of the expression is non-zero.

  • ((123)), ((1 + 1)), ((1 == 1)) return true
  • ((0)), ((-2 + 2)), ((2 == -2)) return false.
  • ((4294967296 * 4294967296)) return false in most shells because of 64 bit integer wrapping

var += 2 as an arithmetic expression, performs the assignment and resolves to the value being assigned, here 0, hence the false exit status.

You can see the value upon which is based the exit status, by using the $((...)) arithmetic expansion syntax:

$ echo "$((1 + 1)) $((2 == 2)) $((2 == -2)) $((var = -2)) $((var += 2))"
2 1 0 -2 0

Or assigning it to a variable:

$ var=-2; ((result = (var += 2)))
$ echo "$? $result $var"
1 0 0

$? contains the exit status of the previous command. As far as if/then/else/fi is concerned, 0 means true, anything else means false.

The confusion here comes from the fact that for arithmetic expressions, it's the other way round: 0 means false and anything else means true (for instance, 2 == 2 is 1 while 2 < 1 is 0).

To avoid worrying about the difference, just forget about $? and its possible values. Just think in terms of boolean true/false, sucess/failure.

 grep -q foo file

Returns true if foo is found in file.

 [ "$a" = "$b" ]

Returns true if $a contains the same thing as $b.

 ((6 * 3 - 12))
 ((4 == 1))

Return true if the result of the arithmetic expression is a non-zero number.

It doesn't matter whether those true/false are expressed in terms of 0 or 1 of the exit status of those grep/[ commands or ((...)) construct.