3

I have a shell script incrementing a variable like in the example below with set -e:

$ var=0; echo $?
0
$ ((var++)); echo $?
1
$ ((var++)); echo $?
0
$ ((var++)); echo $?
0
$ echo $var; echo $?
3
0
$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later  <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.    
$

The script exits unexpectedly. The thing is that the exact same script doesn't exit the same way when running it locally on a MacBook. The bash shell on the MacBook behaves exactly the same way when running the example above.

Does anyone have any clue what is going on here?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Marcel
  • 216
  • I don't see where the script is exiting? – Jeff Schaller Apr 14 '16 at 15:06
  • You seem to have typed the commands on the prompt one by one. Instead can you post the script itself by editing your original question ? – MelBurslan Apr 14 '16 at 15:10
  • 1
    Interesting question, I can reproduce this using a script containing #!/bin/bash; set -e; var=0; echo $?; ((var++)); echo $?; ((var++)); echo $?. I performed some other tests without set -e and it turns out that when incrementing from 0 to 1 the return code is non-zero; when you decrement from 2 to 1 does return zero; decrementing from 0 to -1 returns non-zero and incrementing from -2 to -1 returns 0. – Lambert Apr 14 '16 at 15:15
  • That's exactly the point @Lambert I'm sure the script is exiting because of set -e, BUT, I was surprised by which step was causing the return code to be different than 0. – Marcel Apr 14 '16 at 15:17
  • @Marcel, I am curious too, and why it happens from 0 to 1 and not from 2 to 1. Besides, I think that the local shell on the MacBook is not bash. When I run the script using sh it does not exit. You can force your script to use sh by adding the following as first line: #!/bin/sh – Lambert Apr 14 '16 at 15:24

3 Answers3

5

An arithmetic command is successful if the value of the arithmetic expression is nonzero. If the value of the expression is 0, the command fails with status 1. This allows arithmetic commands to be used in tests, as the boolean operators in shell arithmetic expressions return 0 for true and 1 for false (like in C). For example

if ((x==3)); then …

works because ((x==3)) returns 0 when $x is equal to 3 and 1 otherwise.

The postfix increment operator returns the old value of the variable. So ((var++)) returns an error status if var was previously zero.

set -e tells the shell to exit on the first command that fails. No surprises there.

To avoid unwanted errors resulting from arithmetic expressions that may legitimately have the value 0, don't use arithmetic commands, use an ordinary assignment with an arithmetic expression.

var=$((var+1))
  • Is there a typo in this? I can’t make sense of the first paragraph. If Boolean operators return 0 for true, why should 0 result in a failure with status 1? Seems backwards. – Wildcard Sep 27 '19 at 00:08
  • @Wildcard I don't see a typo, which doesn't mean there isn't one. Yes, it's backwards: C-like expressions use 0 to mean false whereas command statuses use 0 to mean true. – Gilles 'SO- stop being evil' Sep 27 '19 at 06:02
0

You can always use arithmetic commands like ((var++)). If the value of var prior the assignment was 0, then to force a successful status code you can write: ((var++)) || true

Or you can write this to skip the errexit logic: ! ((var++))

0

I use declare -i i=0; i+=1, exit code is zero.

Earlier used to write a hack i=0; ((i++)) ||true

kyb
  • 420