1

I came across this code that echo true if the year is leap, false otherwise. year is the user input that I read in from command line.

(( !(year % 4) && year % 100 || !(year % 400) )) && echo "true" || echo "false"

How is unary ! implemented in bash to produce the desired result?

What I know about return value of arithmetic command: ((10 > 6)); echo "$?" # Output 0. An arithmetic command returns 0 for true.

Can I call the return value of above command, its exit code?

What I know about ! : '!' not only coerces its value to boolean but also flips its parity.

1 Answer 1

4

!(year % 4) in an arithmetic context would first take the result of year % 4, yielding a value between 0 and 3. If the value is 0 (year is a multiple of 4), then the result of the whole operation is 1 (!0), otherwise it is !1, !2, or !3. These all evaluate to 0.

This is standard practice for integer boolean evaluation in most languages.

An operation such as 0 && 3 is false since integer zero is false under boolean arithmetic evaluation.

The exit code of the command (( 10 > 6 )) is zero. This is different from what you get as output from e.g. echo "$(( 10 > 6 ))" (which is 1). An exit code signal "success" or "failure". The test was carried out successfully, so it returns zero. Would you use the comparison in an arithmetic context, its value would be 1:

(( var = 10 > 6 ))
echo "$var"   # prints 1

! outside of an arithmetic context works the same. It turns zero into 1 and non-zero into zero. The interpretation of this by the shell will be different though, with zero being "success" rather than meaning "boolean false" (this may not be a strange thing if you are more accustomed to thinking about shell utilities "not failing" rather than "succeeding").

grep -q -e PATTERN file && echo 'grep did not fail (and found PATTERN)'

! grep -q -e PATTERN file && echo 'grep failed (and did not find PATTERN)'

A zero return code signals "success" in Unix since no further information needs to be conveyed.

A failure is given by a non-zero exit code. The nature of the failure is conveyed by the actual value of the exit code. Valid failure codes are 1-127. The meaning of these should be documented in the utility's manual. Exit statuses above that are for when a utility is terminated by a signal.

6
  • 2
    May be worth also noting that && has precedence over || inside ((...)) and [[...]] but not for the shell && and || shell operators. As in bash -c 'echo "$((1 || 0 && 0))"' ouputs 1, bash -c 'if [[ x || "" && "" ]]; then echo true; else echo false; fi' outputs true but bash -c 'if true || false && false; then echo true; else echo false; fi' outputs false. Often a source of confusion. Dec 11, 2018 at 14:12
  • @StéphaneChazelas What you are saying is that for shell outside ((...)) and [[...]] operators && and || don't function as short circuit operators, is it? Dec 11, 2018 at 14:24
  • 3
    @HarshvardhanSharma. No, I'm saying that a || b && c is a || (b && c) inside and (a || b) && c outside. Dec 11, 2018 at 14:30
  • @StéphaneChazelas ok, so outside ((...)) and [[...]] operators && and || have same precedence and are left associative. Dec 11, 2018 at 14:47
  • @HarshvardhanSharma, yes. See also the corresponding operators in the [, bc, awk, find, expr... commands where YMMV. Dec 11, 2018 at 14:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .