Как определить, не определена ли строка в сценарии оболочки bash? - PullRequest
147 голосов
/ 23 октября 2008

Если я хочу проверить нулевую строку, я бы сделал

[ -z $mystr ]

но что, если я хочу проверить, была ли переменная определена вообще? Или в bash-скриптах нет различий?

Ответы [ 11 ]

131 голосов
/ 23 октября 2008

Я думаю, что ответ, который вы ищете, подразумевается (если не указано) Винко ответ , хотя он не прописан просто. Чтобы определить, установлен ли VAR, но не указан или нет, вы можете использовать:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Вы, вероятно, можете объединить два теста во второй строке в один с:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Однако, если вы прочитаете документацию для Autoconf, вы обнаружите, что они не рекомендуют объединять термины с '-a' и рекомендуют использовать отдельные простые тесты в сочетании с &&. Я не сталкивался с системой, где есть проблема; это не означает, что они не существовали (но они, вероятно, чрезвычайно редки в наши дни, даже если они не были такими редкими в далеком прошлом).

Подробную информацию об этих и других связанных расширениях параметров оболочки , команду test или [ и условные выражения можно найти в Bash ручной.


Меня недавно спросили по электронной почте об этом ответе с вопросом:

Вы используете два теста, и я хорошо понимаю второй, но не первый. Точнее я не понимаю необходимости расширения переменной

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

Разве это не достигнет того же самого?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Справедливый вопрос - ответ «Нет, ваша более простая альтернатива не делает то же самое».

Предположим, я напишу это перед вашим тестом:

VAR=

Ваш тест скажет «VAR вообще не установлен», но мой скажет (косвенно, потому что он ничего не повторяет) «VAR установлен, но его значение может быть пустым». Попробуйте этот скрипт:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

Вывод:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

Во второй паре тестов переменная установлена, но ей присвоено пустое значение. Это различие между обозначениями ${VAR=value} и ${VAR:=value}. То же самое для ${VAR-value} и ${VAR:-value}, ${VAR+value} и ${VAR:+value} и т. Д.


Поскольку Гили указывает в своем ответе , если вы запускаете bash с опцией set -o nounset, тогда базовый ответ, приведенный выше, завершается с unbound variable. Это легко исправить:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Или вы можете отменить параметр set -o nounset, указав set +u (set -u эквивалентно set -o nounset).

35 голосов
/ 23 октября 2008
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z работает и для неопределенных переменных. Чтобы различать неопределенное и определенное, вы должны использовать перечисленные вещи здесь или, с более ясными объяснениями, здесь .

Самый чистый способ - использовать расширение, как в этих примерах. Чтобы ознакомиться со всеми вашими опциями, обратитесь к разделу «Расширение параметров» в руководстве.

Альтернативное слово:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Значение по умолчанию:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

Конечно, вы бы использовали один из них по-другому, поместив желаемое значение вместо «значение по умолчанию» и используя расширение, если это необходимо.

17 голосов
/ 01 октября 2011

Руководство по расширенному написанию сценариев bash, 10.2. Подстановка параметров:

  • $ {var + blahblah}: если переменная определена, вместо «blahblah» выражение, иначе ноль подставляется
  • $ {var-blahblah}: если переменная определена, она сама подставляется, иначе «вздор» заменяется
  • $ {var? Blahblah}: если переменная определена, она подставляется, иначе функция существует с 'blahblah' в качестве сообщения об ошибке.

1011 *
*

Чтобы основать логику вашей программы на том, определена переменная $ mystr или нет, вы можете сделать следующее:

isdefined=0
${mystr+ export isdefined=1}

теперь, если isdefined = 0, тогда переменная была неопределенной, если isdefined = 1, переменная была определена

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


Другие полезные вещи:

иметь значение по умолчанию 7, присвоенное $ mystr, если оно не определено, и оставить его нетронутым в противном случае:

mystr=${mystr- 7}

для вывода сообщения об ошибке и выхода из функции, если переменная не определена:

: ${mystr? not defined}

Остерегайтесь того, что я использовал ':', чтобы содержимое $ mystr не выполнялось как команда, если оно определено.

9 голосов
/ 10 июня 2012

Сводка испытаний.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
5 голосов
/ 15 ноября 2013

https://stackoverflow.com/a/9824943/14731 содержит лучший ответ (более читабельный и работает с включенным set -o nounset). Это работает примерно так:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi
4 голосов
/ 05 июня 2013

Явным способом проверки определяемой переменной будет:

[ -v mystr ]
1 голос
/ 01 декабря 2015

Справочное руководство Bash является официальным источником информации о bash.

Вот пример тестирования переменной, чтобы увидеть, существует ли она:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

раздел 6.3.2 .)

Обратите внимание, что пробел после открытия [ и до ] является , необязательным .


Советы для пользователей Vim

У меня был скрипт, который имел несколько объявлений следующим образом:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Но я хотел, чтобы они отнеслись к любым существующим ценностям. Поэтому я переписал их, чтобы они выглядели так:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

Мне удалось автоматизировать это в vim с помощью быстрого регулярного выражения:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Это можно применить, выбрав соответствующие строки визуально, а затем напечатав :. Панель команд предварительно заполняется :'<,'>. Вставьте указанную выше команду и нажмите Enter.


Проверено на этой версии Vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

Пользователям Windows могут потребоваться разные окончания строк.

1 голос
/ 21 июля 2012

, чтобы не пролить этот велосипед еще дальше, но хотел добавить

shopt -s -o nounset

- это то, что вы можете добавить в начало скрипта, что приведет к ошибке, если переменные не объявлены где-либо в скрипте. Сообщение, которое вы увидите, - unbound variable, но, как отмечают другие, оно не будет перехватывать пустую строку или нулевое значение. Чтобы убедиться, что любое отдельное значение не пустое, мы можем проверить переменную, поскольку она раскрывается с ${mystr:?}, также известным как расширение знака доллара, что приведет к ошибке с parameter null or not set.

1 голос
/ 28 марта 2012

другая опция: расширение "индексы массива списка" :

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

единственный раз, когда это расширяется до пустой строки, это когда foo не установлен, так что вы можете проверить это с условной строкой:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

должно быть доступно в любой версии bash> = 3.0

0 голосов
/ 01 октября 2011

Вот то, что я считаю гораздо более ясным способом проверить, определена ли переменная:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Используйте его следующим образом:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi
...