Статус выхода Bash для сокращенной нотации приращения

Я заметил явное несоответствие в статусе возврата нотации (( )) bash.
Рассмотрим следующее.

$> A=0
$> ((A=A+1))
$> echo $? $A
0 1

Однако использование другой известной сокращенной записи приращения дает:

$> A=0
$> ((A++))
$> echo $? $A
1 1

Если в скрипте есть встроенный set -e, вторая нотация вызовет выход из скрипта, поскольку статус выхода ((A++)) вернул ненулевое значение. Этот вопрос был более или менее рассмотрен в этот связанный вопрос. Но, похоже, это не объясняет разницу в статусе выхода для двух обозначений ((A=A+1)) и ((A++)).

((A++)), похоже, возвращает 1 тогда и только тогда, когда A равно 0. (Отказ от ответственности: я не проводил исчерпывающих тестов. Тестировалось в bash 4.1.2 и 4.2.25). Итак, последний вопрос сводится к следующему:

Почему A=0; ((A++)) возвращает 1?


person Pankrates    schedule 15.03.2014    source источник


Ответы (2)


a++ является постинкрементным: он увеличивается после оценки оператора. Напротив, ++a увеличивается раньше. Таким образом:

$ a=0 ; ((a++)) ; echo $a $?
1 1
$ a=0 ; ((++a)) ; echo $a $?
1 0

В первом случае, ((a++)), сначала вычисляется арифметическое выражение, в то время как a по-прежнему равно нулю, что дает нулевое значение (и, следовательно, ненулевой статус возврата). Затем, после этого, увеличивается a.

Во втором случае ((++a)), a увеличиваются до 1, а затем оценивается ((...)). Поскольку a не равно нулю при вычислении арифметического выражения, статус возврата равен нулю.

От man bash:

   id++ id--
          variable post-increment and post-decrement
   ++id --id
          variable pre-increment and pre-decrement
person John1024    schedule 15.03.2014
comment
Теперь это кажется таким очевидным, спасибо. Однако это, по-видимому, означает, что всякий раз, когда кто-то использует set -e, невозможно безопасно использовать любую стенографическую запись для счетчика, если ожидается, что счетчик потенциально может принимать значение -1 или 0. Есть ли способ обойти это? Или следует просто отказаться от сокращенной записи - person Pankrates; 16.03.2014
comment
@Pankrates, многие участники #bash предлагают отказаться от использования set -e. См. mywiki.wooledge.org/BashFAQ/105. Тем не менее, вы также можете запустить (( a++ )) ||: - person Charles Duffy; 16.03.2014
comment
@Charles очень полезная ссылка, спасибо. Однако вывод статьи еще не определен. С одной стороны, не используйте его, используйте свою собственную проверку ошибок, а не используйте его, но остерегайтесь ошибок, как показано в моем исходном вопросе. Я бы предпочел использовать set -e (думаю, для удобства), но только если есть исчерпывающий список упомянутых ошибок. Поскольку у меня его нет (если такой список вообще существует), думаю, я воспользуюсь вашим советом :) - person Pankrates; 16.03.2014
comment
Да -- я сказал очень многие, а не все, поскольку консенсус разделился -- я на самом деле использую его, если знаком с предостережениями. OTOH, Грег - основной сопровождающий вики, на котором размещен этот FAQ - находится на том конце дебатов, которого лучше всего избегать. - person Charles Duffy; 16.03.2014

Статус выхода нотации (()) равен нулю, если арифметическое выражение не равно нулю, и наоборот.

A=A+1

Вы присваиваете 1 A, поэтому выражение оценивается как 1, выход из состояния ноль.

A++

Оператор POST-инкремента. Выражение оценивается как ноль, статус выхода равен 1, а затем A увеличивается.

person SzG    schedule 15.03.2014