Як я можу перевірити, чи змінна порожня або містить лише пробіли?


240

Наступний синтаксис bash перевіряє, чи paramвін не порожній:

 [[ !  -z  $param  ]]

Наприклад:

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

Немає результату та його штрафу.

Але коли paramпорожньо, окрім одного (або більше) символів пробілу, справа в іншому:

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

"Я не нуль" виводиться.

Як я можу змінити тест, щоб вважати змінні, які містять лише символи пробілу, як порожні?


4
Від man test: -z STRING - the length of STRING is zero. Якщо ви хочете видалити всі пробіли $param, скористайтеся${param// /}
dchirikov

6
ЯК не існує простої trim()функції, вбудованої до будь-якого * nix? Стільки різних хакків, щоб досягти чогось такого простого ...
ADTC

6
@ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ string) мені здається досить простим. Навіщо винаходити колесо?
jbowman

3
Бо це могло бути блискучішим
Інверс

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сценаріях.


використання sedвони сказали ... просто echoзмінна, яку вони сказали ...
mikezter

1
Хм. Якщо це так, ${var/pattern/string}чи не слід, [[ -z ${param/ /} ]]щоб пробіл між косою рисою був візерунком, а нічого після цього не було рядком?
Джессі Чизгольм

@JesseChisholm "Оскільки заміна порожня, ми можемо опустити остаточне / та значення рядка"; "Якщо рядок є нульовим, збіги шаблону видаляються, і / наступний шаблон може бути опущений."
Майкл Гомер

6
@MichaelHomer Відповідь [[ -z "${param// }" ]]впевнена, схоже, що patternпорожня, і replacement stringце єдиний пробіл. А-а! Я бачу це зараз, if pattern begins with a slashя пропустив цю частину. тож шаблон є, / а рядок залишається і, отже, порожній. Дякую.
Джессі Чисгольм

bash note: якщо запуск у set -o nounset (set -u), а param не встановлений (а не нульовий або заповнений пробілом), то це призведе до помилки незмінної змінної. (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, що ти спробував? 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вас варіант .)


1
Це трохи заплутано. Звичайний портативний спосіб перевірити , чи містить рядок певних символів є зcase .
Жиль

@Gilles Я не думаю, що можна написати caseглобус, щоб виявити рядок, який порожній або містить лише символи пробілу. (Це було б легко з regexp, але caseне робить regexps. Конструкція у вашій відповіді не буде працювати, якщо ви піклуєтесь про нові рядки.)
zwol

1
Я наводив пробіли як приклад у своїй відповіді, тому що саме це запитання було задано. Це ж легко перевірити пробільні символи: case $param in *[![:space:]]*) …. Якщо ви хочете протестувати IFSсимволи, ви можете використовувати шаблон *[$IFS]*.
Жиль

1
@mikeserv У дійсно серйозному захисному сценарії ви явно встановлюєте IFS <space><tab><newline>на початку, а потім більше ніколи не торкайтесь його. Я думав, що це буде відволікання.
zwol

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}

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 та працює у будь-якому нащадку Борна.

Якщо ви хочете розширити це також на вкладки, включіть явну вкладку:

 [ -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, lksh та posh.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.