Как проверить, установлена ​​ли переменная в Bash? - PullRequest
1308 голосов
/ 30 августа 2010

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

Например, как я могу проверить, передал ли пользователь первый параметр функции?

function a {
    # if $1 is set ?
}

Ответы [ 32 ]

14 голосов
/ 01 февраля 2012

Прочитайте раздел «Расширение параметров» справочной страницы bash.Расширение параметра не обеспечивает общий тест для устанавливаемой переменной, но есть несколько вещей, которые вы можете сделать с параметром, если он не установлен.

Например:

function a {
    first_arg=${1-foo}
    # rest of the function
}

установит first_arg равным $1, если он назначен, в противном случае он использует значение "foo".Если a обязательно должен принимать один параметр, и хорошего значения по умолчанию не существует, вы можете выйти с сообщением об ошибке, если параметр не указан:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(обратите внимание на использование : в качестве нулевогокоманда, которая просто расширяет значения своих аргументов. Мы не хотим ничего делать с $1 в этом примере, просто завершите работу, если она не установлена)

14 голосов
/ 31 августа 2010

Чтобы проверить, установлена ​​ли переменная с непустым значением, используйте [ -n "$x" ], как уже указали другие.

В большинстве случаев рекомендуется рассматривать переменную с пустым значением так же, как переменную, которая не установлена. Но вы можете различить их, если вам нужно: [ -n "${x+set}" ] ("${x+set}" расширяется до set, если установлено x, и до пустой строки, если x не установлено).

Чтобы проверить, был ли передан параметр, протестируйте $#, который представляет собой число параметров, переданных функции (или сценарию, когда отсутствует в функции) (см. Ответ Павла ) .

13 голосов
/ 08 февраля 2016

В bash вы можете использовать -v внутри [[ ]] встроенного:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR
12 голосов
/ 16 ноября 2016

Для тех, кто ищет для проверки неустановленное или пустое в сценарии с set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

Обычная проверка [ -z "$var" ] завершится неудачно с var; unbound variable, если set -u, но [ -z "${var-}" ] расширяет до пустой строки, если var сбрасывается без ошибок.

9 голосов
/ 15 февраля 2016

Примечание

Я даю сильный ответ, ориентированный на Bash, из-за тега bash.

Короткий ответ

Пока вы только имеете делос именованными переменными в Bash, эта функция всегда должна сообщать вам, была ли установлена ​​переменная, даже если это пустой массив.

is-variable-set() {
    declare -p $1 &>dev/null
}

Почему это работает

В Bash (по крайней мере, какЕще до 3.0), если var является объявленной / установленной переменной, то declare -p var выводит команду declare, которая установит переменную var равной ее текущему типу и значению, и возвращает код состояния 0(успех).Если var не объявлено, то declare -p var выводит сообщение об ошибке на stderr и возвращает код состояния 1.Используя &>/dev/null, перенаправляет как обычный stdout, так и stderr вывод на /dev/null, никогда не будет видно и без изменения кода состоянияТаким образом, функция возвращает только код состояния.

Почему другие методы (иногда) не работают в Bash

  • [ -n "$var" ]: Это проверяет только, если${var[0]} не пусто.(В Bash $var совпадает с ${var[0]}.)
  • [ -n "${var+x}" ]: Это только проверяет, установлен ли ${var[0]}.
  • [ "${#var[@]}" != 0 ]: Это только проверяет, установлен ли хотя бы один индекс $var.

Когда этот метод не работает в Bash

Это работает толькодля именованных переменных (включая $_), не определенных специальных переменных ($!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., и все, что я, возможно, забыл).Поскольку ни один из них не является массивом, POSIX-стиль [ -n "${var+x}" ] работает для всех этих специальных переменных.Но остерегайтесь оборачивать его в функцию, так как многие специальные переменные изменяют значения / существование при вызове функций.

Примечание о совместимости оболочки

Если в вашем скрипте есть массивы, и вы пытаетесь сделать его совместимымс как можно большим количеством оболочек, затем рассмотрите возможность использования typeset -p вместо declare -p.Я читал, что ksh поддерживает только первое, но не смог проверить это.Я знаю, что Bash 3.0+ и Zsh 5.5.1 поддерживают оба typeset -p и declare -p, отличающиеся только тем, что одно является альтернативой для другого.Но я не проверял различия между этими двумя ключевыми словами и не проверял другие оболочки.

Если вам нужен ваш скрипт, чтобы он был совместим с POSIX sh, то вы не можете использовать массивы.Без массивов [ -n "{$var+x}" ] работает.

Код сравнения для различных методов в Bash

Эта функция сбрасывает переменную var, eval s переданный код, запускает тесты для определения, если var устанавливается кодом eval d и, наконец, показывает итоговые коды состояния для различных тестов.

Я пропускаю test -v var, [ -v var ] и [[ -v var ]], поскольку они дают идентичные результатыпо стандарту POSIX [ -n "${var+x}" ], при этом требуется Bash 4.2+.Я также пропускаю typeset -p, потому что он совпадает с declare -p в оболочках, которые я тестировал (Bash 3.0 - 5.0 и Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Код тестового примера

Обратите внимание, что результаты теста могут быть неожиданными из-за того, что Bash рассматривает индексы нечислового массива как «0», если переменная не была объявлена ​​как ассоциативный массив.Кроме того, ассоциативные массивы действительны только в Bash 4.0 +.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Тестовый вывод

Тестовые мнемоники в строке заголовка соответствуют [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ]declare -p var, соответственно.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Резюме

  • declare -p var &>/dev/null надежно (100%?) для проверки именованных переменных вBash, поскольку по крайней мере 3.0.
  • [ -n "${var+x}" ] надежен в POSIX-совместимых ситуациях, но не может обрабатывать массивы.
  • Существуют другие тесты для проверки, является ли переменная непустой,и для проверки объявленных переменных в других оболочках.Но эти тесты не подходят ни для скриптов Bash, ни для POSIX.
5 голосов
/ 19 октября 2014

Использование [[ -z "$var" ]] - это самый простой способ узнать, установлена ​​ли переменная или нет, но эта опция -z не различает неустановленную переменную и переменную, для которой задана пустая строка:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Лучше проверить это в соответствии с типом переменной: переменная env, параметр или обычная переменная.

Для переменной env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Для параметра (например, проверить наличие параметра $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Для обычной переменной (используя вспомогательную функцию, чтобы сделать это элегантным способом):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Примечания:

  • $#: дает количество позиционных параметров.
  • declare -p: дает определение переменной, переданной в качестве параметра. Если он существует, возвращает 0, если нет, возвращает 1 и печатает сообщение об ошибке.
  • &> /dev/null: подавляет вывод из declare -p без влияния на его код возврата.
4 голосов
/ 30 августа 2010

Вы можете сделать:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}
4 голосов
/ 09 ноября 2013

Ответы выше не работают, если включена опция Bash set -u.Кроме того, они не являются динамическими, например, как проверить, определена ли переменная с именем «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 проверить, определена ли переменная в режиме "-u"

2 голосов
/ 15 ноября 2014

Мой предпочтительный способ такой:

$var=10
$if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$unset var
$if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Таким образом, в основном, если переменная установлена, она становится «отрицанием результирующего false» (то, что будет true = "установлено").

И, если он не установлен, он станет" отрицанием результирующего true "(так как пустой результат оценивается как true) (поэтому закончится как false ="НЕ установлено").

1 голос
/ 22 сентября 2014

В оболочке вы можете использовать оператор -z, который равен True, если длина строки равна нулю.

Простая однострочная строка для установки значения по умолчанию MY_VAR, если она не установлена, в противном случае выможет отображать сообщение:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...