Увеличение переменной вызывает EXIT в bash 4, но не в bash 3

Рассмотрим этот (примерный) скрипт bash:

#!/bin/bash -e
errorExit() {
    echo "" >&2
    echo "ERROR (${var_scriptfilename}):" >&2
    echo "An unhandled error occurred." >&2
    intentionalExit 1
}
intentionalExit () {
    trap - EXIT # Unregister the EXIT trap
    exit $1
}
trap errorExit EXIT # Trap script errors
var_scriptfilename="$(basename "$0")"
# ==== START OF TEST ====
var_counter=0
((var_counter++))
echo "var_counter is $var_counter" >&2
# ===== END OF TEST =====
intentionalExit 0

Если я запускаю его в Cygwin's bash, он выдает намеченный результат:

var_counter is 1

Тем не менее, если я запустил его на своем поле Debian Squeeze, которое является его предназначением, я попаду в ловушку EXIT:

ERROR (test.increment.sh):
An unhandled error occurred.

...Это почему?

Если я уберу опцию -e, она будет работать, как и ожидалось, в обеих системах, но я хочу, очевидно, использовать -e.

Немного более громоздкий "универсальный" вариант, var_counter=$(($var_counter+1)), работает с -e, установленным на обеих оболочках, но я бы предпочел использовать первую нотацию (или что-то похожее), поскольку она явно выступает в качестве операции приращения при чтении кода.

bash --version на Cygwin Bash говорит:

GNU bash, version 3.2.51(24)-release (i686-pc-cygwin)
Copyright (C) 2007 Free Software Foundation, Inc.

На Debian это:

GNU bash, Version 4.1.5(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.

Я заинтригован, почему это так. Кто-нибудь знает причину такого поведения?

Кроме того, кто-нибудь знает похожий способ увеличения переменной в bash, который я мог бы использовать?

1 ответ

Решение

С man-страницы bash4 в Debian:

((expression))
    The expression is evaluated according  to  the  rules  described
    below  under ARITHMETIC EVALUATION.  If the value of the expres‐
    sion is non-zero, the return status is 0; otherwise  the  return
    status is 1.  This is exactly equivalent to let "expression".

а также...

-e      Exit  immediately  if a pipeline (which may consist of a
        single simple command),  a subshell command enclosed  in
        parentheses,  or one of the commands executed as part of
        a command list enclosed by  braces  (see  SHELL  GRAMMAR
        above) exits with a non-zero status.

Так что происходит ((var++)) увеличивает переменную от 0 до 1 и возвращает 0, в результате чего общее выражение возвращает ненулевое значение, что вызывает errexit,

Теперь о разнице между двумя разными версиями Bash: это изменение в (( поведение, по-видимому, произошло между 4,0 и 4,1. В 4.0 (( по-видимому, не вызывает errexit. Смотрите этот файл NEWS для деталей. Вам придется прокрутить вниз до строки 135 или около того. Список изменений из исходного дистрибутива, кажется, подтверждает это.

Если вы просто хотите, чтобы переменная увеличивалась без использования состояния выхода, есть несколько способов сделать это. Может быть, некоторые другие люди могут дать совет, какой из них лучше, но некоторые возможности:

  • var="$((var+1))"Портативный POSIX sh метод
  • ((var++)) || true, заставляя оператор всегда иметь нулевой статус выхода (только bash)
Другие вопросы по тегам