Портативный способ проверки пустоты переменной оболочки

Что такое переносимый и канонический способ проверить, является ли переменная пустой / undefined в оболочке script? Он должен работать во всех sh-подобных оболочках. То, что я делаю сейчас, это что-то вроде:

if [ -z "$var" ] ; then
    ...

и для обратного, делая что-то, когда переменная не пуста / undefined:

if [ -n "$var" ] ; then
    ...

И пока эти работы для скриптов, которые я пишу сейчас, , я хотел бы знать способ, который будет работать в любой разумно совместимой sh-подобной оболочке, даже в некоторой более неясной среде, чем Linux PC с GNU userland и bash или тире.

Я ищу "экспертный ответ" от кого-то, кто имеет опыт работы в разных средах оболочки, и знает подводные камни разных способов делать что-то, а не такое мнение, как "это должно работать".

Ответы

Ответ 1

Отказ от ответственности. Я не считаю себя экспертом в оболочках.

AFAIK, команда test и флаги -z и -n являются частью стандарта POSIX, поэтому вы должны быть в порядке, используя их на многих платформах.

Ответ 2

Конструкция кронштейна [...] является частью стандарта 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, конечно, не нуждается в них.) Поэтому, вероятно, можно игнорировать это.

Ответ 3

Важно помнить, что пустая переменная оболочки - это не то же самое, что и неустановленная переменная оболочки, хотя это различие не может быть выполнено с использованием любой формы [ 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]}

  • Указывает на ошибку, если Null или Unset. Если параметр 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

Ответ 4

Вам не нужно идти, если есть еще лестница, если у вас нет больших кодов, работающих в тестировании переменных

один пример linner

_JAIL=""
[  -z "$_JAIL" ] && echo "No" || echo "Yes"

Нажмите здесь Дополнительные примеры.

Ответ 5

Портативный и канонический - это две разные вещи.

Для переносимого теста я вижу несколько сценариев autoconf:

if test "x$MY_VAR" = x; then
  # ...
fi

Это тесты для пустоты, а не отсутствия.