В Bash, как я могу проверить, определена ли переменная в режиме "-u"

Я только что обнаружил set -u в bash, и это помогло мне найти несколько ранее невидимых ошибок. Но у меня также есть сценарий, где мне нужно проверить, определена ли переменная до вычисления значения по умолчанию. Лучшее, что я придумал для этого:

if [ "${variable-undefined}" == undefined ]; then
    variable="$(...)"
fi

который работает (пока переменная не имеет строкового значения undefined). Мне было интересно, есть ли лучший способ?

Ответ 1

Что не работает: проверка строк с нулевой длиной

Вы можете протестировать строки undefined несколькими способами. Использование стандартного условного условного выражения выглядит следующим образом:

# Test for zero-length string.
[ -z "$variable" ] || variable='foo'

Это не будет работать с set -u.

Что работает: условное назначение

В качестве альтернативы вы можете использовать условное присваивание, что более похоже на Bash. Например:

# Assign value if variable is unset or null.
: "${variable:=foo}"

Из-за того, как Bash обрабатывает расширение этого выражения, вы можете безопасно использовать его с set -u, не получая ошибку "bash: переменная: несвязанная переменная".

Ответ 2

Это то, что я нашел, работает лучше всего для меня, принимая во внимание другие ответы:

if [ -z "${varname-}" ]; then
  ...
  varname=$(...)
fi

Ответ 3

В bash 4.2 и новее существует явный способ проверить, установлена ​​ли переменная, которая должна использовать -v. Пример из вопроса может быть реализован следующим образом:

if [[ ! -v variable ]]; then
   variable="$(...)"
fi

См. http://www.gnu.org/software/bash/manual/bashref.html#Bash-Conditional-Expressions

Если вы хотите только установить переменную, если она еще не установлена, вам, вероятно, лучше что-то делать по этим строкам:

variable="${variable-$(...)}"

Обратите внимание, что это не относится к определенной, но пустой переменной.

Ответ 4

Ответы выше не являются динамическими, например, как проверять переменную с именем "dummy"? Попробуйте следующее:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Связано: Как проверить, установлена ​​ли переменная в Bash?

Ответ 5

В начале вашего script вы можете определить свои переменные с пустым значением

variable_undefined=""

Тогда

if [ "${variable_undefined}" == "" ]; then
    variable="$(...)"
fi

Ответ 6

Unfortunatly [[ -v variable ]] не поддерживается в более старых версиях bash (по крайней мере, не в версии 4.1.5. У меня есть Debian Squeeze)

Вместо этого вы можете использовать дополнительную оболочку:

if (true $variable)&>/dev/null; then
    variable="$(...)"
fi

Ответ 7

if [ "${var+SET}" = "SET" ] ; then
    echo "\$var = ${var}"
fi

Я не знаю, как далеко назад поддерживается ${var + value}, но он работает, как минимум, в 4.1.2. В старых версиях не было ${var + value}, они имели только ${var: + value}. Разница в том, что ${var: + value} будет оцениваться только до "value", если $var устанавливается в непустую строку, а ${var + value} также будет оцениваться как "значение", если $var устанавливается в пустую строку.

Без [[-v var]] или ${var + value} Я думаю, вам придется использовать другой метод. Вероятно, тест подоболочки, описанный в предыдущем ответе:

if ( set -u; echo "$var" ) &> /dev/null; then
    echo "\$var = ${var}
fi

Если ваш shell-процесс уже активирован "set -u" , он также будет активен в подоболочке без необходимости "set -u" , но включение его в команду subhell позволяет также работать, если родительский процесс не включил "set -u" .

(Вы также можете использовать другой процесс, такой как "printenv" или "env", чтобы проверить наличие переменной, но тогда он будет работать, только если экспортируется переменная.)