2

According to the bash man page,

while list-1; do list-2; done The while command continuously executes the list list-2 as long as the last command in the list, list-1, returns an exit status of zero.

I'm trying to make sense of how bash is interpreting the exit codes of arithmetical expressions when they appear at the end of list-1. I started with some experiments:

$ echo $((1))                                                                                                                                                                                                             
1                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ ((1)); echo $?                                                                                                                                                                                                          
0                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ echo $((! 1))
0                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ ((! 1)); echo $?  # Hm, what does this exit code mean?
1                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ while ((1)); do echo "yes"; break; done                                                                                     
yes 

# This could be responding to either the exit code of ((1))
# or to it's value, 1 meaning true
                                                                                                                                                                                                                        
$ while ((! 1)); do echo "yes"; break; done                                                                                        
                                                                                                                                                                                                                        
# This could be responding to either the exit code of ((! 1)), 
# or to it's value, 0 meaning false
                                                                                                                                                                                                                        
$ while ! ((1)); do echo "yes"; break; done                                                                                                                                                                               
                                                                                                                                                                                                                        
# this ! should change the exit code, not the value, 
# so it appear to be responding to the exit code
                                                                                                                                                                                                                        
                                                                                                                                                                                                                        
                                                                                                                                                                                                                        
$ echo $((0))                                                                                                                                                                                                             
0                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ ((0)); echo $?  # Hm, what does this exit code mean?
1                      
                                                                                                                                                                                                                        
$ echo $((! 0)))                                                                                                                                                                                                          
1                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ ((! 0)); echo $?                                                                                                                                                                                                        
0                                                                                                                                                                                                                       
                                                                                                                                                                                                                        
$ while ((0)); do echo "yes"; break; done

# This could be responding to either the exit code of ((0)) 
# or to it's value, 0 meaning false
                                                                                                                                                                                                                        
$ while ((! 0)); do echo "yes"; break; done                                                                                                                                                                               
yes                                                                                                                                                                                                                     
                                                                                                                                                                                                                        
# This could be responding to either the exit code of ((! 0)) 
# or to it's value, 1 meaning true                                                                                                                                      
                                                                                                                                                                                                                        
$ while ! ((0)); do echo "yes"; break; done                                                                                                                                                                               
yes                                                                                                                                                                                                                     
                                                                                                                                                                                                                        
# this ! should change the exit code, not the value, 
# so it appears to be responding to the exit code.

So, after fixing two errors (thanks @Barmar), there is a consistent interpretation based on the while loop looking at exit codes. What remains obscure is why the exit code of ((0)) is failure, and the exit code of ((1)) is success.

I came across this while trying to capture exit codes of functions in the while condition for use when the loop breaks:

while <function>; do
    <stuff>
done

# I need the exit code of <function> here.

So, perhaps this way:

while <function>; ((! (RESULT=$?) )); do  # alternative ! (( RESULT=$? ))
    <stuff>
done
Diagon
  • 473
  • 5
  • 16
  • FYI: `while` with an immediate `break` is the same as `if`. – Barmar Nov 13 '21 at 00:37
  • Thanks. The `while` I'm actually running loops more than once, so this is simplified, but I supposed I could have used an `if` to distill it even further. – Diagon Nov 13 '21 at 00:39
  • When I do `((! 1)); echo $?` I get `1`. – Barmar Nov 13 '21 at 00:44
  • @Barmar Hm. That one you are right about. Let me see what that means ... – Diagon Nov 13 '21 at 00:45
  • Relevant: https://unix.stackexchange.com/questions/32250/why-does-a-0-let-a-return-exit-code-1/32251#32251 – Barmar Nov 13 '21 at 00:46
  • `((0))` is a conditional that evaluates `0` (`true`) and returns `1` correctly. `((! 0))` is a conditional that evaluates `1` (`false`) and returns `0` appropriately. – David C. Rankin Nov 13 '21 at 01:26
  • Does this answer your quesiton https://stackoverflow.com/questions/28705927/why-shell-uses-0-as-success https://stackoverflow.com/questions/68555944/logic-operator-evaluation-in-bash/68556053#68556053 ? – KamilCuk Nov 13 '21 at 03:23
  • Thanks @KamilCuk , but that answer doesn't explain how true/false is coded in bash to relate to success/vailure. The other answers here do explain that. – Diagon Nov 13 '21 at 09:47
  • 1
    Hm, "is coded in bash" it's just like that, from https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-let `If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1`, there's nothing more to explain. If you really are interested in "how is coded", then https://github.com/bminor/bash/blob/f3a35a2d601a55f337f8ca02a541f8c033682247/builtins/let.def#L108 . – KamilCuk Nov 13 '21 at 09:50
  • Ok, I see that now in the `man` page, but I don't see it at that stackoverflow answer that you linked. Thanks for that. It's right there, but I missed it. – Diagon Nov 13 '21 at 10:22

2 Answers2

2

What bash is doing is associating ((1)), which evaluates to 1, to "truth". It then makes that consistent with exit codes by returning an exit code of success, 0. Similarly, it is associating ((0)), which evaluates to 0, to "false". It them makes that consistent with exit codes by return the exit code of failure, 1.

This may seem confusing, as after all both evaluations ((.)) are "successful", but this is a hack to bring the value of arithmetic expressions representing true/false in line with bash's exit codes of success/failure, and make conditional expressions like if ...; then ...; fi, while ...; do ...; done, etc, work correctly.

Diagon
  • 473
  • 5
  • 16
1

Anything that evaluates to zero is false, anything that does not is true.

It means you can do things like:

while :; do
    ((c++%5)) || ((val=RANDOM))
    echo "$val changes every 5 iterations"
    sleep 0.5
done

Negative numbers are not zero:

$ ((20-30)); echo $?
0
$ ((20-20)); echo $?
1
$ ((20-10)); echo $?
0

For ((! 0)), the logical negation of 0 is 1, and ((1)) returns true (0). For !((0)), the return value false (1), is negated by the (non arithmetic) operator !, causing the return value of the arithmetic subshell to be true (0) instead of false.

dan
  • 4,846
  • 6
  • 15