Подтвердить что ты не робот

Bash: использование `true`

Во многих сценариях, которые я унаследовал от бывшего сотрудника, я продолжаю видеть этот шаблон:

if (true $SOME_VAR)&>/dev/null; then
    ...
fi

или этот

(true $SOME_VAR)&>/dev/null || SOME_VAR="..."

Страница man для true говорит, что он всегда возвращает true, поэтому я продолжаю задаваться вопросом, в чем суть этих проверок? В первом случае часть then всегда выполняется, во втором случае правая часть никогда не выполняется.

4b9b3361

Ответ 1

Если действует set -u (a.k.a. set -o nounset), true $SOME_VAR не будет работать, если $SOME_VAR не определен. Поэтому это способ проверить, определена ли переменная.

Ответ 2

Чтобы дополнить полезный ответ jwodder и полезный ответ Фреда:

  • В Bash v4.2 + , менее сложный и более эффективный -v оператор может использоваться для тестирования, если определена переменная [1] (обратите внимание, что no $):
    [[ -v SOME_VAR ]]

  • В версиях старше Bash и в сценариях, совместимых с POSIX, используйте подход, основанный на расширении параметра Fred, который также более эффективен, чем подход (true ...).

  • Если целью является просто предоставить значение по умолчанию, как в идиоме (true $SOME_VAR)&>/dev/null || SOME_VAR="...", используйте метод (POSIX-совместимый), предложенный kojiro, также основанное на расширении параметра :
    SOME_VAR=${SOME_VAR-...} # keep $SOME_VAR value or default to '...'
    Тоби Спейт предлагает другой вариант, совместимый с POSIX, ${SOME_VAR=...}, который напрямую обновляет переменную значением по умолчанию, если это undefined; однако он имеет побочный эффект расширения до (итогового) значения - что может быть или не быть желательным. Конкретный, но также слегка неясный способ подавления расширения состоит в том, чтобы передать расширение в двоеточие (null) утилита (:), который расширяется, но в противном случае игнорирует его аргументы (по сравнению с использованием true для этой же цели, возможно, немного менее запутанным):
    : ${SOME_VAR=...} # set $SOMEVAR to '...' only if not defined

  • Обратите внимание, что все описанные выше расширения параметров имеют вариант , который помещает : перед оператором, который затем действует не только тогда, когда переменная undefined но также, когда он определен, но пустой (содержит пустую строку):
    ${SOME_VAR:+...}, ${SOME_VAR:-...}, ${SOME_VAR:=...}
    Вероятно, это вариантное поведение является более надежным методом, особенно учитывая, что когда set -u (set -o nunset) не включается, переменные undefined расширяются до нулевой (пустой) строки.

Чтобы добавить к объяснению jwodder:

  • Использование (...) вокруг true $SOME_VAR для создания подоболочки имеет решающее значение для этого несколько неясного теста для существования переменной, чтобы работать по назначению.

    • Без подоболочки весь script будет прерван.

    • Потребность в подоболочке делает технику не просто неясной, но и неэффективной (хотя это не будет заметно при случайном использовании).

      • Кроме того, , если set -u (set -o nounset) не действует, метод рассматривает все переменные как определенные.
    • С подоболочкой прерывается только подоболочка, которая отражается в ее коде выхода на текущую оболочку: 1, если подоболочка прервана (переменная не существует), 0 в противном случае. < ш > Поэтому команда (true ...) оценивает (концептуально) true, если переменная существует.

    • &>/dev/null подавляет сообщение об ошибке из подоболочки, которая испускается, если переменная не существует.

      • В стороне: true никогда не выводит результат, поэтому достаточно использовать (true $SOME_VAR)2>/dev/null (только подавлять только stderr) - это изменение делает технику совместимой с POSIX (хотя все еще нецелесообразной).
  • Внутри script есть не просто выражения set -u (set -o nounset), которые в случае доступа к переменной undefined включают прерывание bash с опцией командной строки -u имеет тот же эффект.


[1] Поскольку Bash v4.3, вы также можете проверить, имеет ли переменная массива элемент с указанным индексом; например.:
a=( one two ); [[ -v a[0] ]] выполняется успешно, поскольку существует элемент массива с индексом 0; работает аналогично ассоциативным массивам.

Ответ 3

Следующее, вероятно, эквивалентно и более просто:

if [ "${SOME_VAR+x}" ] then
    ...
fi

Или в случае назначения:

[ "${SOME_VAR+x}" ] || SOME_VAR="..."

Оператор расширения + расширяется до нулевой строки, если переменная не установлена, и x, если она назначена (назначена нулевая строка по-прежнему означает присвоенную). В этом случае вы можете заменить x на то, что хотите (кроме пустой строки).

Существует также вариант ${SOME_VAR:+x}. Разница заключается в нулевых строках: :+ расширяется до нулевой строки, если переменной присваивается пустая строка (while + расширяется до x, если значение назначено, даже если это пустая строка).

Ответ 4

Хотя это не то же самое,

if [ x"$SOME_VAR" = x ]; then
    ...
fi

стремится делать то, что вы хотите; это if, если true, если $SOME_VAR - undefined или (разность:), определенная как строка нулевой длины.

Этот код не работает, если SOME_VAR не установлен и -u установлен. Я считаю, что следующий башизм работает: "${SOME_VAR-}" = "".