Как узнать, не определена ли строка в сценарии оболочки bash?
Вопрос
Если я хочу проверить нулевую строку, я бы сделал
[ -z $mystr ]
но что, если я захочу проверить, определена ли вообще переменная?Или в сценариях bash нет различий?
Решение
Я думаю, что ответ, который вы ищете, подразумевается (если не указан) Винко
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 установлен, но его значение может быть пустым & Quot ;. Попробуйте этот скрипт:
(
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
. Это легко исправить:
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
Или вы можете отменить параметр unbound variable
, указав set +u
(set -u
эквивалентно <=>).
Другие советы
~> 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
Конечно, вы бы использовали один из них по-другому, поместив желаемое значение вместо «значение по умолчанию» и используя расширение, если это необходимо.
Расширенное руководство по написанию сценариев bash, 10.2.Замена параметра:
- ${вар+блабла}:Если вар определяется, «блахблах» заменяется выражением, иначе заменяется нуль
- ${вар-блабла}:Если вар определен, он сам заменяется, иначе «блахблах» заменяется
- ${вар?блабла}:Если VAR определяется, это заменяется, иначе функция существует с «блахблай» в качестве сообщения об ошибке.
Чтобы логика вашей программы основывалась на том, определена ли переменная $mystr или нет, вы можете сделать следующее:
isdefined=0
${mystr+ export isdefined=1}
теперь, если isdefined=0, то переменная не определена, если isdefined=1, то переменная определена
Этот способ проверки переменных лучше, чем приведенный выше ответ, потому что он более элегантен, удобочитаем, и если ваша оболочка bash настроена на ошибку при использовании неопределенных переменных. (set -u)
, сценарий завершится преждевременно.
Другие полезные вещи:
чтобы присвоить $mystr значение по умолчанию 7, если оно не определено, и оставить его нетронутым в противном случае:
mystr=${mystr- 7}
чтобы напечатать сообщение об ошибке и выйти из функции, если переменная не определена:
: ${mystr? not defined}
Имейте в виду, что я использовал ':', чтобы содержимое $mystr не выполнялось как команда в случае, если оно определено.
Сводка тестов.
[ -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"
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
Явным способом проверки определяемой переменной будет следующий:
[ -v mystr ]
другой вариант: список &; индексы массива " Расширение : р>
$ 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
не пролить этот байк еще дальше, но хотел добавить
shopt -s -o nounset
это то, что вы можете добавить в начало скрипта, что приведет к ошибке, если переменные не объявлены где-либо в скрипте. Вы увидите сообщение unbound variable
, но, как отмечают другие, оно не будет перехватывать пустую строку или нулевое значение. Чтобы убедиться, что любое отдельное значение не пустое, мы можем проверить переменную, поскольку она раскрывается с помощью ${mystr:?}
, также известного как расширение знака доллара, что приводит к ошибке с parameter null or not set
.
Справочное руководство по Bash является официальным источником информации о bash.
Вот пример тестирования переменной, чтобы увидеть, существует ли она:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(From раздел 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 могут потребоваться разные окончания строк.
Вот то, что я считаю гораздо более простым способом проверить, определена ли переменная:
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
вызов установлен без аргументов .. он выводит все определенные переменные ..
последними в списке будут те, которые определены в вашем скрипте ..
чтобы вы могли направить его вывод во что-то, что могло бы определить, какие вещи определены, а какие нет