bash set -e и i = 0, пусть i ++ не согласен - PullRequest
12 голосов
/ 30 августа 2011

следующий скрипт с опцией отладки 'set -e -v' завершается с ошибкой в ​​операторе приращения, только если переменная имеет предшествующее значение ноль.

#!/bin/bash
set -e -v
i=1; let i++; echo "I am still here"
i=0; let i++; echo "I am still here"

i=0; ((i++)); echo "I am still here"

bash (GNU bash, версия 4.0.33 (1) -релиз (x86_64-apple-darwin10), но также GNU bash, версия 4.2.4 (1) -релиз (x86_64-unknown-linux-gnu))

есть идеи?

Ответы [ 3 ]

19 голосов
/ 31 августа 2011

ответ на мой вопрос заключается не в использовании let (или shift , или ...), а в использовании

i=$((i+1))

при попытке проверить скрипт bash, установив ' выход с ненулевым кодом состояния ' с помощью

set -e

Руководство по bash гласит, что set -e имеет эффект ' Выйти немедленно, если простая команда завершается с ненулевым статусом. '.

К сожалению let shift и ...) возвращают результат вычисления (' Если последний аргумент оценивается как 0, пусть возвращает 1; 0 равен возвращено иначе '). Таким образом, вместо кода состояния можно получить возвращаемое значение некоторого вида. И иногда это возвращаемое значение будет равно нулю, а иногда - одному в зависимости от вычисления. Поэтому установка -e приведет к выходу скрипта в зависимости от результата ваших вычислений !!! и с этим ничего не поделаешь, если вы не используете его никогда или прибегаете к

let i++ || true

как указано arnaud576875, что, кстати, добавляет дополнительную нагрузку на процессор.

Использование

let ++i

работает только для конкретного случая, в котором i не равно -1, как в случае let i ++ , который работает только в том случае, когда i не равно 0. Следовательно, полу-решения.

Хотя я люблю Unix, у меня не было бы другого пути.

9 голосов
/ 30 августа 2011

Если последний аргумент let оценивается как 0, пусть возвращает 1 (так, ненулевой статус):

Из руководства:

   let arg [arg ...]

Каждый аргумент представляет собой арифметическое выражение для оценки. Если последний аргумент оценивается как 0, пусть возвращает 1 ; В противном случае возвращается 0.

i++ оценивается в ноль, когда i равен 0 (потому что это постинкремент, поэтому возвращается предыдущее значение i), поэтому let возвращает 1, и из-за set -e, bash существует.

Вот несколько решений:

let ++i         # pre-increment, if you expect `i` to never be -1
let i++ 1       # add an expression evaluating to non-zero
let i++ || true # call true if let returns non-zero
0 голосов
/ 30 августа 2011

Просмотр страницы BASH на set -e:

Выйти немедленно, если простая команда (см. ОБРАЗЕЦ ОБОЛОЧКИ выше) завершается с ненулевым статусом. [...]

Таким образом, если какой-либо оператор возвращает ненулевой код выхода, оболочка завершит работу.

Посмотрите на справочную страницу BASH , на команду let:

Если последний аргумент оценивается как 0, пусть возвращает 1; В противном случае возвращается 0.

Но подождите! Ответ на i++ - один, а не ноль! Это должно было сработать!

Опять же, ответом является справочная страница BASH в операторе приращения:

id ++ id--: переменная после увеличения и после уменьшения

Хорошо, не так ясно. Попробуйте этот сценарий оболочки:

#!/bin/bash
set -e -v
i=1; let ++i; echo "I am still here"
i=0; let ++i; echo "I am still here"

i=0; ((++i)); echo "I am still here"

Хммм ... это работает как ожидалось, и все, что я сделал, это изменил i++ на ++i в каждой строке.

i++ - это оператор после увеличения . Это означает, что он увеличивается i после того, как оператор let возвращает значение. Поскольку i равнялся нулю до увеличения , оператор let возвращает ненулевое значение.

Однако ++i является оператором предварительного увеличения . Это означает, что он увеличивает i перед возвратом состояния выхода. Поскольку i увеличивается до 1, состояние выхода становится равным нулю.

Надеюсь, это имеет смысл.

...