Как я могу проверить, является ли переменная пустой или содержит только пробелы?

240

Следующий синтаксис bash проверяет, paramне является ли он пустым:

 [[ !  -z  $param  ]]

Например:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

Нет выхода и его хорошо.

Но когда paramпусто, за исключением одного (или более) пробела, тогда дело обстоит иначе:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

«Я не ноль» выводится.

Как изменить тест, чтобы переменные, содержащие только пробелы, рассматривались как пустые?

maihabunash
источник
4
От man test: -z STRING - the length of STRING is zero. Если вы хотите удалить все пробелы в $param, используйте${param// /}
dchirikov
6
WOW вообще нет простой trim()функции, встроенной в любой * nix? Так много разных хаков для достижения чего-то такого простого ...
ADTC
6
@ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ string) мне кажется достаточно простым. Зачем изобретать велосипед?
jbowman
3
Потому что это могло бы быть более блестящим
Inversus
1
Просто отметив, что [[ ! -z $param ]]эквивалентно test ! -z $param.
Ноа Суссман

Ответы:

292

Во-первых, обратите внимание, что -zтест явно для:

длина строки равна нулю

То есть строка, содержащая только пробелы, не должна быть истинной -z, так как имеет ненулевую длину.

Вы хотите удалить пробелы из переменной, используя расширение параметра замены шаблона :

[[ -z "${param// }" ]]

Это расширяет paramпеременную и заменяет все совпадения шаблона (один пробел) ничем, поэтому строка, содержащая только пробелы, будет расширена до пустой строки.


Суть в том, как это работает, - то, что ${var/pattern/string}заменяет первый самый длинный матч patternс string. Когда patternначинается с /(как указано выше), он заменяет все совпадения. Поскольку замена пуста, мы можем опустить финал /и stringзначение:

$ {Параметр / шаблон / строка}

Шаблон расширяется , чтобы получить шаблон так же , как в расширении имени файла. Параметр раскрывается, и самое длинное совпадение шаблона с его значением заменяется строкой . Если шаблон начинается с '/', все совпадения шаблона заменяются строкой . Обычно заменяется только первый матч. ... Если строка пуста, совпадения с шаблоном удаляются и / следующий шаблон может быть опущен.

После всего этого мы в конечном итоге ${param// }удаляем все пробелы.

Обратите внимание, что, хотя присутствует в ksh(где он возник), zshи bashчто этот синтаксис не POSIX и не должен использоваться в shсценариях.

Майкл Гомер
источник
1
используйте sedони сказали ... только echoпеременную, которую они сказали ...
mikezter
1
Хм. Если это так, не ${var/pattern/string}должно ли быть [[ -z ${param/ /} ]]пространство между слешами, чтобы быть шаблоном, и ничто после того, чтобы быть строкой?
Джесси Чисхолм
@JesseChisholm "Поскольку замена пуста, мы можем опустить final / и строковое значение"; «Если строка пуста, совпадения с шаблоном удаляются, и / следующий шаблон может быть опущен».
Майкл Гомер
6
@MichaelHomer Ответ [[ -z "${param// }" ]], безусловно , выглядит так, как будто patternпусто и replacement stringявляется одним пробелом. AH! Теперь я вижу, что if pattern begins with a slashпропустил ту часть. таким образом, шаблон / и строка оставлены и, следовательно, пустые. Благодарю.
Джесси Чисхолм
примечание bash: если выполняется в set -o nounset (set -u), а параметр не задан (а не равен нулю или заполнен пробелом), то это приведет к ошибке несвязанной переменной. (set + u; .....) освобождает этот тест.
Брайан Крисман
31

Самый простой способ проверить, что строка содержит только символы в авторизованном наборе, - это проверить наличие несанкционированных символов. Таким образом, вместо проверки, содержит ли строка только пробелы, проверьте, содержит ли строка какой-либо символ, отличный от пробела. В bash, ksh или zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

«Только состоит из пробелов» включает случай пустой (или неустановленной) переменной.

Вы можете проверить наличие любого символа пробела. Используется [[ $param = *[^[:space:]]* ]]для использования настроек локали или любого другого явного списка пробельных символов, для которого вы хотите проверить, например, [[ $param = *[$' \t\n']* ]]для проверки пробела, табуляции или новой строки.

Сопоставление строки с шаблоном с =внутренним [[ … ]]является расширением ksh (также присутствует в bash и zsh). В любом стиле Bourne / POSIX вы можете использовать caseконструкцию для сопоставления строки с шаблоном. Обратите внимание, что стандартные шаблоны оболочки используют !для отрицания набора символов, а не ^как в большинстве синтаксисов регулярных выражений.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Чтобы проверить наличие пробельных символов, $'…'синтаксис специфичен для ksh / bash / zsh; Вы можете вставить эти символы в ваш скрипт буквально (обратите внимание, что символ новой строки должен быть в кавычках, так как обратная косая черта + символ новой строки расширяется до нуля), или генерировать их, например

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac
жилль
источник
Это почти превосходно, но пока не отвечает на поставленный вопрос. В настоящее время он может сказать вам разницу между неустановленной или пустой переменной оболочки и переменной, которая содержит только пробелы. Если вы примете мое предложение, вы добавите форму раскрытия параметров, еще не представленную любым другим ответом, который явно проверяет переменную оболочки на предмет ее установки - я имею в виду, ${var?not set}что прохождение этого теста и выживание гарантируют, что переменная, соответствующая вашей *) case, произведет определенное действие ответ, например.
mikeserv
Исправление - это, фактически, ответило бы на первоначально заданный вопрос, но с тех пор оно было отредактировано таким образом, что оно в корне меняет смысл вопроса и, следовательно, лишает законной силы ваш ответ.
mikeserv
Вы нуждаетесь [[ $param = *[!\ ]* ]]в mksh.
Стефан Шазелас
20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

Для того, чтобы различать пустой , не пустым , пустой , снята с охраны :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]предназначен для горизонтальных символов пробела (пробел и табуляция в ASCII, но, возможно, в вашей локали есть еще несколько; некоторые системы будут включать неразрывный пробел (где это возможно), некоторые не будут). Если вы также хотите использовать вертикальные символы (например, перевод строки или перевод строки), замените их [:blank:]на [:space:].

Стефан Шазелас
источник
POSIXly идеален, потому что он будет работать с (возможно, лучше) тире.
Кен Шарп
zshКажется, есть проблема с [![:blank:]], это повышение :bнедопустимый модификатор. В shи kshподражать, это поднять событие не найдено[
cuonglm
@cuonglm, что ты пробовал? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'это нормально для меня. Событие не найдено будет только для интерактивных оболочек.
Стефан Шазелас
@ StéphaneChazelas: Ах да, я попробовал с интерактивными оболочками. :bНедействителен Модификатор немного странно.
cuonglm
1
@FranklinYu, на OS / X , shбудет bashпостроен с , --enable-xpg-echo-defaultи --enable-strict-posix-defaultтак, чтобы быть совместимым Unix (который включает в себя echo '\t'вывод вкладки).
Стефан Шазелас
4

Единственная оставшаяся причина написать сценарий оболочки вместо сценария на хорошем языке сценариев - это то, что крайняя переносимость - это первостепенная проблема. Наследие /bin/sh- единственное, в чем вы можете быть уверены, но Perl, например, с большей вероятностью будет доступен кроссплатформенно, чем Bash. Поэтому никогда не пишите сценарии оболочки, которые используют функции, которые не являются действительно универсальными, и имейте в виду, что несколько проприетарных поставщиков Unix заморозили свою среду оболочки до POSIX.1-2001 .

Существует портативный способ сделать этот тест, но вы должны использовать tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(Значение по умолчанию для $IFSудобства - пробел, табуляция и новая строка.)

( printfВстроенная система сама по себе портативна, но полагаться на нее гораздо проще, чем выяснять, какой у echoвас вариант .)

zwol
источник
1
Это немного запутанно. Обычный портативный способ проверить, содержит ли строка определенные символы, - с помощьюcase .
Жиль
@ Жиль Я не думаю, что можно написать caseглобус, чтобы обнаружить строку, которая либо пуста, либо содержит только пробельные символы. (Это было бы легко с регулярным выражением, но caseне выполняет регулярные выражения . Конструкция в вашем ответе не сработает, если вы заботитесь о переводах строки.)
zwol
1
Я привел пробелы в качестве примера в своем ответе, потому что это то, что задал вопрос. Это же легко проверить пробельные символы: case $param in *[![:space:]]*) …. Если вы хотите проверить на IFSсимволы, вы можете использовать шаблон *[$IFS]*.
Жиль
1
@mikeserv В действительно серьезном сценарии защиты вы явно устанавливаете IFS <space><tab><newline>в начале, а затем никогда больше не трогаете его. Я думал, что это будет отвлекать.
Звол
1
Что касается переменных в шаблонах, я думаю, что это работает во всех версиях Bourne и всех оболочках POSIX; это потребовало бы дополнительного кода, чтобы обнаружить шаблоны случая и отключить расширение переменной, и я не могу придумать причину, чтобы сделать это. У меня есть несколько примеров этого в моем исполнении, .profileкоторое было выполнено многими снарядами Борна. Конечно [$IFS], не будет делать то, что было задумано, если IFSимеет определенные значения не по умолчанию (пустые (или неустановленные), содержащие ], начиная с !или ^).
Жиль
4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi
Гуру
источник
это избавляет от любого символа пробела, а не только от одного пробела, верно? спасибо
Александр Миллс
0

Для проверки, если переменная пуста или содержит пробелы, вы также можете использовать этот код:

${name:?variable is empty}
Pratik
источник
2
Я думаю, что ваш ответ опущен, потому что «или содержат пробелы» не соответствует действительности.
Бернхард
будьте осторожны - это убивает текущую оболочку. Лучше положить его в (: $ {subshell?}). Кроме того - это будет писать в stderr - вы, вероятно, хотите перенаправить. И самое важное, что оценивает фактическое значение переменной, если оно не установлено или равно нулю. Если там есть команда, вы просто ее выполнили. Лучше всего провести этот тест :.
mikeserv
как это убьет раковину. и вы также можете проверить это, сначала определите, name=aaaaзатем запустите эту команду, echo ${name:?variable is empty}она напечатает значение имени переменной, и теперь, запустив эту команду, echo ${name1:?variable is empty}она напечатает -sh: name1: переменная пуста
pratik
Да - echo ${name:?variable is empty}либо получит $nameненулевое значение, либо убьет оболочку. Так что по той же причине, которую вы не делаете, prompt > $randomvarне следует ${var?}- если там есть команда, она, вероятно, будет выполняться - и вы не знаете, что это такое. Интерактивная оболочка не должна выходить - вот почему ваша нет. Тем не менее, если вы хотите увидеть некоторые тесты, их много здесь. , Этот не такой длинный ...
mikeserv
0

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

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Объяснение:

  • Расширение ${param%%[! ]*}удаляет от первого не пробела до конца.
  • Это оставляет только ведущие места.
  • Следующее расширение удаляет начальные пробелы из переменной.
  • Если результат пустой, то переменная либо пуста для начала.
  • Или, если переменная не была пустой, удаление начальных пробелов делало ее пустой.
  • Если результат пустой -z, то эхо empty.

Вышесказанное совместимо с POSIX и работает в любом потомке Bourne.

Если вы хотите расширить это также на вкладки, включите явную вкладку:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

или используйте переменную с символами, которые вам нужно удалить:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

или вы можете использовать некоторое POSIX «символьное выражение класса» (пробел означает пробел и табуляцию):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Но это не удастся в MKSH, LKH и шикарный.

Исаак
источник
-1

Попробуй это:

$ [[ -z \`echo $n\` ]] && echo zero

zero
Хьюго
источник