Проверка строки ненулевой длины в Bash: [-n «$ var»] или [«$ var»]

183

Я видел тесты Bash-скриптов для строки с ненулевой длиной двумя разными способами. Большинство сценариев используют -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
AllenHalsey
источник

Ответы:

396

Редактировать: это более полная версия, которая показывает больше различий между [(ака 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

Если вы хотите узнать, имеет ли переменная ненулевую длину, выполните одно из следующих действий:

  • заключите переменную в квадратные скобки (столбец 2a)
  • используйте -nи заключите в кавычки переменную (столбец 4a)
  • используйте двойные скобки с или без кавычек и с или без -n(столбцы 1b - 4b)

Обратите внимание, что в столбце 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
Спасибо! Я решил использовать стиль «заключать переменную в кавычки (столбец 2a)» IMO, -n просто добавляет шум и уменьшает читабельность. Аналогично, для тестирования нулевой длины или неустановленного значения я буду использовать [! "$ var"] вместо [-z "$ var"].
AllenHalsey
2
Итак, ваша диаграмма для ["против [-n"(первый вопрос ОП) показывает, что они полностью эквивалентны, верно?
вар
1
@hobs: Да, и то, что использовать, зависит от того, что более понятно. Вы также заметите, что их эквивалент при использовании формы двойных скобок, который предпочтительнее при использовании Bash.
Приостановлено до дальнейшего уведомления.
1
Итак, чтобы подвести итог вашей удивительной таблицы, более ясный («лучший») способ проверки на ненулевые строки[["
hobs
23

Лучше использовать более мощный, [[ что касается Bash.

Обычные случаи

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 по поводу [[ $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

Связанные посты и документация

Есть много сообщений, связанных с этой темой. Вот несколько из них:

codeforester
источник
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

Обратите внимание на использование [[...]], которое правильно обрабатывает кавычки переменных для вас.

user2592126
источник
2
Зачем использовать, -nкогда это не очень нужно в Bash?
codeforester
0

Альтернативный и, возможно, более прозрачный способ оценки пустой переменной среды - использовать ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
user1310789
источник
10
Это очень старая школа со времен Bourne Shell. Не увековечивай эту старую привычку. bashявляется более острым инструментом, чем его предшественники.
tbc0
2
Вам даже не нужно это с оболочками перед bash, если вы избегаете синтаксиса, который стандарт POSIX явно помечает как устаревший. [ "$ENV_VARIABLE" != "" ]будет работать на каждой оболочке с POSIX-совместимой testреализацией - не только bash, но и ash / dash / ksh / etc.
Чарльз Даффи
... то есть не использовать -aили -oкомбинировать тесты, но вместо этого использовать [ ... ] && [ ... ]или [ ... ] || [ ... ]и единственные угловые случаи, которые могут применяться к использованию данных произвольных переменных в тесте, однозначно закрыты.
Чарльз Даффи
-2

Используйте case/esacдля проверки:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
источник
12
Пожалуйста нет. Это не то, что caseпредназначено для.
tbc0
2
caseлучше всего работает, когда есть более двух альтернатив.
codeforester
caseне предназначен для этого вида использования.
Луис Лавир