Ответ 1
Отказ от ответственности. Я не считаю себя экспертом в оболочках.
AFAIK, команда test
и флаги -z
и -n
являются частью стандарта POSIX, поэтому вы должны быть в порядке, используя их на многих платформах.
Что такое переносимый и канонический способ проверить, является ли переменная пустой / undefined в оболочке script? Он должен работать во всех sh-подобных оболочках. То, что я делаю сейчас, это что-то вроде:
if [ -z "$var" ] ; then
...
и для обратного, делая что-то, когда переменная не пуста / undefined:
if [ -n "$var" ] ; then
...
И пока эти работы для скриптов, которые я пишу сейчас, , я хотел бы знать способ, который будет работать в любой разумно совместимой sh-подобной оболочке, даже в некоторой более неясной среде, чем Linux PC с GNU userland и bash или тире.
Я ищу "экспертный ответ" от кого-то, кто имеет опыт работы в разных средах оболочки, и знает подводные камни разных способов делать что-то, а не такое мнение, как "это должно работать".
Отказ от ответственности. Я не считаю себя экспертом в оболочках.
AFAIK, команда test
и флаги -z
и -n
являются частью стандарта POSIX, поэтому вы должны быть в порядке, используя их на многих платформах.
Конструкция кронштейна [...]
является частью стандарта POSIX и безопасна почти везде. Итак, чтобы проверить, является ли переменная пустой, случай if
, используемый в вопросе, идеален:
if [ -z "$STRING" ]; then
# $STRING is empty
fi
Пока вы помните, что:
Некаблированная переменная подвержена разбиению слов, поэтому если вы используете [ -z $STRING ]
(без кавычек вокруг $STRING
) и $STRING
содержит пробелы или пусто, оболочка увидит [ -z SEVERAL WORDS ]
или [ -z ]
, которые являются одновременно синтаксическими ошибками - то есть не то, что вы хотите.
Альтернативный способ, если тестирование, если строка в оболочке POSIX пуста, заключается в использовании =
.
if [ "$STRING" = "" ]; then
# $STRING is empty
fi
Обратите внимание, что вы не должны использовать двойные знаки равенства (==
) внутри [...]
! (Это работает только bash
, или когда sh
является ссылкой на bash
- но если вы запускаете на другой машине, где sh
ссылается на что-то еще, это не сработает.) Если вы хотите использовать двойной знак равенства, вы также должны иметь двойные скобки [[...]]
(который работает только в zsh
, bash
и т.д., но не в обычных оболочках POSIX, таких как dash
).
&&
или ||
внутри [...]
Он может работать в некоторых оболочках, но он не будет работать во всех них. Вместо этого для " и" напишите [...] && [...]
или [... -a ...]
. И для " или" напишите [...] || [...]
или [... -o ...]
.
Иногда в действительно древних сценариях оболочки вы увидите такие вещи, как следующее, для проверки пустой переменной. Это все еще отлично (но выглядит неудобно в моих глазах), и я никогда не сталкивался с оболочкой, которая требует этого (хотя, по общему признанию, мой опыт ограничен вне Linux - возможно, есть BSD-оболочки, которые все еще нуждаются в этом?)
if [ ".$STRING" = "." ]; then
# $STRING is empty
fi
Предполагая период, следует избегать путаницы [...]
, если $STRING
содержит -e
, -o
и другие параметры, которые использует [...]
. Добавляя период, он превращается в необязательный вариант.
Я никогда не видел, чтобы оболочка нуждалась в этом обходном пути. (Dash, конечно, не нуждается в них.) Поэтому, вероятно, можно игнорировать это.
Важно помнить, что пустая переменная оболочки - это не то же самое, что и неустановленная переменная оболочки, хотя это различие не может быть выполнено с использованием любой формы [ test ]
. Все, что test
может сделать, это сообщить о значении расширения переменной - это определение параметра, которое определяет это. Например:
var=value
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently not empty.
Опять же:
var=
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
###OUTPUT###
I am $var and I am set. I am currently empty.
С [ test ]
:
{ _nstr() { echo 'I just evaluated a ""null string!' ; }
[ -z "$var" ] && _nstr
[ -z "" ] && _nstr
unset var
[ -z "$var" ] && _nstr
echo "${var+I am \$var and I am set. I am currently ${var:+not} empty.}"
}
###OUTPUT###
I just evaluated a ""null string!
I just evaluated a ""null string!
I just evaluated a ""null string!
Надеюсь, это приведет его домой:
var=value
[ -z "${var+}" ] && _nstr
###OUTPUT###
I just evaluated a ""null string!
Если вы хотите с уверенностью определить, является ли переменная пустой, это сделает следующее:
${var:+false} [ -n "${var+1}" ] && echo \$var is set and null
Я думаю, вы могли бы очень хорошо использовать расширение параметра оболочки для таких случаев, а в некоторых случаях даже убить двух зайцев одним выстрелом (см. первый пример). Это, безусловно, должно быть переносимым. Ниже следует неполная копия/вставка из открытых команд оболочки POSIX, найденных здесь: http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
${parameter:-word}
unset
или null
, расширение слова должно быть заменено; в противном случае значение параметра должно быть заменено. ${parameter:=word}
unset
или null
, расширение слова должно быть присвоено параметру. Во всех случаях конечное значение параметра должно быть заменено. Таким образом можно назначить только переменные, а не позиционные параметры или специальные параметры. ${parameter:?[word]}
unset
или null
, расширение слова (или сообщение, указывающее, что оно не установлено, если слово опущено) должно быть записано в стандартную ошибку, и оболочка выходит с ненулевым статусом выхода. В противном случае значение параметра должно быть заменено. Интерактивная оболочка не должна выходить. ${parameter:+word}
unset
или null
, следует заменить null
; в противном случае расширение слова должно быть заменено.<сильные > ПРИМЕРЫ:
${parameter:-word}
ls
выполняется, только если x
- null
или unset
. (Обозначение замещения команды $( ls)
объясняется в Command Substitution.)
${x:-$(ls)}
${parameter:=word}
% unset X
% echo ${X:=abc}
abc
EDIT:
Я только что заметил Ашиша с одним лайнером, и потому, что мне показалось, что это просто дешево, просто вставьте это, я бы хотел что-то сделать. Итак, здесь один-линейный расширитель установки POSIX:
_JAIL="" ; echo "_JAIL is ${_JAIL:-unset or null.}"
Ну, еще дешево, я думаю.
EDIT2:
Итак, я просто заметил это: "... пока эти работы для скриптов, которые я пишу сейчас, я хотел бы знать, что будет работать в любой разумно совместимой оболочке sh-like, даже в некоторой более неясной среде чем Linux-ПК с GNU userland и bash или тире..."
Мне жаль, что я лично не специалист, но я сначала подобрал следующий метод определения важных переменных оболочки из аргументов, которые могут быть или не быть переданы после того, как выкапывали некоторые довольно умело написанные скрипты initramfs busybox. Он будет работать так, как ожидалось, почти везде.
Это красиво, потому что вы только устанавливаете переменные в состояние по умолчанию, если пользователь не определяет их без какой-либо дополнительной работы, чтобы проверить, действительно ли они это сделали. Конечно, есть тонны других применений, я просто недостаточно умен, чтобы использовать их так, как должен.
_VAR1=${1:-/default/path} ; _VAR2=${2:-"$_default_size"}
EDIT3:
phs было прав, чтобы указать, что его метод проверяет пустые переменные, которые не присутствуют в вопросе. Этот метод может делать то же самое в зависимости от того, используется ли центральная двоеточие. Я скопировал из руководства POSIX ниже своего рода таблицу дерьма, чтобы продемонстрировать это.
{subs='substitute'; params='parameters'; assn='assign'; err='error'}
"$parameter" == |set && !null |set && null| !set
------------------ ------------- ----------- ----------
${parameter:-word} | subs params | subs word | subs word
------------------ ------------- ----------- ----------
${parameter-word} | subs params | subs null | subs word
------------------ ------------- ----------- ----------
${parameter:=word} | subs params | assn word | assn word
------------------ ------------- ----------- ----------
${parameter=word} | subs params | subs null | assn word
------------------ ------------- ----------- ----------
${parameter:?word} | subs params | err; exit | err; exit
------------------ ------------- ----------- ----------
${parameter?word} | subs params | subs null | err; exit
------------------ ------------- ----------- ----------
${parameter:+word} | subs word | subs null | subs null
------------------ ------------- ----------- ----------
${parameter+word} | subs word | subs word | subs null
Вам не нужно идти, если есть еще лестница, если у вас нет больших кодов, работающих в тестировании переменных
один пример linner
_JAIL=""
[ -z "$_JAIL" ] && echo "No" || echo "Yes"
Нажмите здесь Дополнительные примеры.
Портативный и канонический - это две разные вещи.
Для переносимого теста я вижу несколько сценариев autoconf:
if test "x$MY_VAR" = x; then
# ...
fi
Это тесты для пустоты, а не отсутствия.