BASH: (( i++ )) vs (( ++i )) и другие примеры арифметических операторов

Автор: | 19/06/2014
 

terminalПредположим, у нас есть простой цикл:

a=0
while (( a<5 )); do
  echo $a
  (( a++ ))
done

На первый взгляд – все верно и должно работать. Давайте запустим:

$ cat plus
#!/usr/bin/env bash

echo -e "nFirst loopn"
a=0
while (( a<5 )); do
  echo $a
  (( a++ ))
done
$ ./plus

First loop

0
1
2
3
4

Но – если мы добавим set -o errexit то увидим совсем другую картину:

$ cat plus
#!/usr/bin/env bash

set -o errexit

echo -e "nFirst loopn"
a=0
while (( a<5 )); do
  echo $a
  (( a++ ))
done
$ ./plus

First loop

0

В debug-режиме всё будет выглядеть ещё интереснее:

$ bash -x plus
+ set -o errexit
+ echo -e 'nFirst loopn'

First loop

+ a=0
+ ((  a<5  ))
+ echo 0
0
+ ((  a++  ))

Почему так? Давайте добавим вывод ошибок:

$ cat plus
#!/usr/bin/env bash

set -o errexit

echo -e "nFirst loopn"
a=0
while (( a<5 )); do
  echo $a
  (( a++ )) && { echo Report: $?; echo -e "PlusOKn"; } || { echo Report: $?;  echo -e "PlusERRn"; }
done

И запустим:

$ ./plus

First loop

0
Report: 1
PlusERR

Как видим – при первой итерации выражение (( 0 + 1 )) возвращает ошибку, из-за чего выполнение цикла и прерывается, т.к. установлена опция errexit.

Решение – либо не использовать errexit, либо – использовать другие арифметические выражения.

Вот несколько примеров:

$ cat plus
#!/usr/bin/env bash

#set -o errexit

echo -e "nFirst loopn"
a=0
while (( a<3 )); do
  echo $a
  (( a++ )) && { echo Report: $?; echo -e "PlusOKn"; } || { echo Report: $?;  echo -e "PlusERRn"; }
done

echo -e "nSecond loopn"
b=0
while (( b<3 )); do
  echo $b
  (( ++b )) && { echo Report: $?; echo -e "PlusOKn"; } || { echo Report: $?;  echo -e "PlusERRn"; }
done

echo -e "nThrid loopn"
c=0
while (( c<3 )); do
  echo $c
  c=$(( c+1 )) && { echo Report: $?; echo -e "PlusOKn"; } || { echo Report: $?;  echo -e "PlusERRn"; }
done

echo -e "nFourth loopn"
d=0
while (( d<3 )); do
  echo $d
  let "d=d+1" && { echo Report: $?; echo -e "PlusOKn"; } || { echo Report: $?;  echo -e "PlusERRn"; }
done

И выполнение – обратите внимание на отсутствие ошибок в остальных циклах:

$ ./plus

First loop

0
Report: 1
PlusERR

1
Report: 0
PlusOK

2
Report: 0
PlusOK


Second loop

0
Report: 0
PlusOK

1
Report: 0
PlusOK

2
Report: 0
PlusOK


Thrid loop

0
Report: 0
PlusOK

1
Report: 0
PlusOK

2
Report: 0
PlusOK


Fourth loop

0
Report: 0
PlusOK

1
Report: 0
PlusOK

2
Report: 0
PlusOK