Увеличение переменной вызывает EXIT в bash 4, но не в bash 3 - PullRequest
6 голосов
/ 29 июля 2011

Рассмотрим этот (примерный) 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 Ответ

9 голосов
/ 29 июля 2011

Из справочной страницы 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)
...