Тест на рядок ненульової довжини в Bash: [-n “$ var”] або [“$ var”]


182

Я бачив тести сценаріїв Баша на рядок із нульовою довжиною двома різними способами. Більшість сценаріїв використовують -nопцію:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

Але варіант -n насправді не потрібен:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

Який кращий спосіб?

Аналогічно, що є кращим способом тестування на нульову довжину:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

або

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi

Відповіді:


394

Редагувати: Це більш повна версія, яка показує більше відмінностей між [(ака test) та [[.

Наведена нижче таблиця показує, що цитується чи ні змінна, ви використовуєте одинарні чи подвійні дужки та чи змінна містить лише пробіл - це те, що впливає на те, чи використання тесту з чи без -n/-zпідходить для перевірки змінної.

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

Якщо ви хочете дізнатися, чи є змінною ненульову довжину, виконайте одну з наступних дій:

  • цитувати змінну в окремих дужках (стовпець 2а)
  • використання -nі цитування змінної в окремих дужках (стовпець 4а)
  • використовувати подвійні дужки з або без котирування та з або без -n(стовпці 1б - 4б)

Зауважте, що у стовпці 1a, починаючи з рядка, позначеного "два", що результат вказує на те, що [оцінюється вміст змінної так, ніби вони були частиною умовного виразу (результат відповідає твердженню, що має на увазі "T" або "F" у стовпець опису). Коли [[використовується (стовпчик 1b), змінна зміст розглядається як рядок і не оцінюється.

Помилки в стовпцях 3a та 5a викликані тим, що значення змінної включає пробіл, а змінна не цитується. Знову, як показано у стовпцях 3b та 5b, [[оцінюється вміст змінної як рядок.

Відповідно, для тестів на рядки нульової довжини, стовпці 6a, 5b та 6b показують правильні способи цього зробити. Також зауважте, що будь-який із цих тестів може бути заперечений, якщо заперечення виявляє чіткішу намір, ніж використання протилежних операцій. Наприклад: if ! [[ -n $var ]].

Якщо ви користуєтесь [, головне, щоб не отримати несподіваних результатів, - це цитування змінної. Використовувати [[це не має значення.

Повідомлення про помилки, які пригнічуються, є "очікується, що оператор очікується" або "очікується бінарний оператор".

Це сценарій, який склав таблицю вище.

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF

1
Дякую! Я вирішив прийняти стиль "цитувати змінну в одинарних дужках (колонка 2а)" IMO, -n просто додає шуму і зменшує читабельність. Точно так само для тестування нульової довжини чи зняття ззовні я використаю [! "$ var"] замість [-z "$ var"].
AllenHalsey

2
Тож ваша діаграма ["проти vs [-n"(перше питання ОП) показує, що вони абсолютно рівнозначні, правда?
варильні панелі

1
@hobs: Так, і що використовувати, залежить від того, що є більш зрозумілим. Ви також помітите, що їх еквівалент при використанні форми з подвійною дужкою, яка є кращою при використанні Bash.
Призупинено до подальшого повідомлення.

1
Отже, підсумовуючи свою дивовижну таблицю, більш чіткий ("кращий") спосіб перевірити наявність ненульових рядків[["
варильні панелі

22

Краще використовувати більш потужні [[ , що стосується Баша.

Звичайні випадки

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

Дві вищезгадані конструкції виглядають чистими і читабельними. Їх повинно вистачити в більшості випадків.

Зауважте, що нам не потрібно цитувати розширення змінних всередині, [[оскільки немає небезпеки розщеплення слів і поглинання .

Щоб запобігти shellcheck «S м'яких скарг [[ $var ]]і [[ ! $var ]], ми могли б використовувати -nваріант.

Рідкісні випадки

У рідкісному випадку, коли нам доводиться розрізняти "встановлення порожнього рядка" проти "взагалі не встановлене", ми можемо використовувати наступні:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

Ми також можемо використовувати -vтест:

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

Попутні повідомлення та документація

Існує маса публікацій, пов’язаних з цією темою. Ось декілька:


9

Ось ще кілька тестів

Вірно, якщо рядок не порожній:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

Вірно, якщо рядок порожній:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"

Це не відповідає на питання ОП про те, що є кращим способом.
codeforester

0

Правильна відповідь така:

if [[ -n $var ]] ; then
  blah
fi

Зверніть увагу на використання параметра [[...]], яке правильно обробляє цитування змінних для вас.


2
Навіщо використовувати, -nколи це не дуже потрібно в Bash?
codeforester

0

Альтернативним і, можливо, більш прозорим способом оцінки порожньої середовища змінної є використання ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi

10
Це дуже стара школа з часів Борна. Не увічнюйте цю стару звичку. bashє більш гострим інструментом, ніж його попередники.
tbc0

2
Вам навіть не потрібно це з попередніми ударами оболонок, якщо ви уникаєте синтаксису, який стандарт POSIX явно позначає застарілим. [ "$ENV_VARIABLE" != "" ]буде працювати над кожною оболонкою із POSIX-сумісною testреалізацією - не просто bash, але ash / dash / ksh / тощо.
Чарльз Даффі

... тобто, не використовуйте -aабо -oне комбінуйте тести, а натомість використовуйте [ ... ] && [ ... ]або, [ ... ] || [ ... ]і єдині кутові випадки, які можуть застосовуватися до використання даних довільних змінних у тесті, однозначно закриваються.
Чарльз Даффі

-2

Використовувати case/esacдля тестування:

case "$var" in
  "") echo "zero length";;
esac

12
Ні, будь ласка. Це не те, що caseпризначено.
tbc0

2
caseнайкраще працює, коли існує більше двох альтернатив.
codeforester

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