Я хочу проверить, является ли параметр пустой строкой "".

Если параметр не установлен, тест должен завершиться неудачно.

  1. Почему следующее не удается?

    $ unset aa
    $ if [ ${aa}=="" ]; then echo yes; else echo no; fi
    yes
    
  2. Что мне делать вместо этого?

1
LinuxSecurityFreak 28 Янв 2018 в 10:55
Вы использовали слово «параметр» в отношении вашего недавнего вопроса?
 – 
Jeff Schaller
28 Янв 2018 в 00:03
4
Является ли отсутствие пробела вокруг == опечаткой?
 – 
ilkkachu
28 Янв 2018 в 00:20
@don_crissti, ${aa?}? это вызовет ошибку, если aa не установлен?
 – 
ilkkachu
28 Янв 2018 в 00:26
Не могли бы вы уточнить, почему пробелы должны быть вокруг ==?
 – 
Tim
28 Янв 2018 в 00:45
Да.
 – 
Tim
28 Янв 2018 в 00:46

3 ответа

Лучший ответ

test, то есть [, требует пробелов вокруг операторов, в основном потому, что в противном случае foo== будет восприниматься как содержащий оператор, даже если это допустимая строка. В этом случае конструкция [[ .. ]] работает так же. (См. этот вопрос и BashFAQ 031 о разнице между [ и [[.)

Таким образом, [ ${aa}=="" ] всегда будет истинным, поскольку [ string ] совпадает с [ -n string ], т. е. проверяет, что строка не пуста. И здесь строка содержит как минимум ==.

Тогда [ ${aa} == "" ] будет ошибкой, если aa не установлен или пуст, поскольку пустая переменная, раскрытая без кавычек, исчезает, а [ == foo ] не является допустимым тестом. Если aa имеет непустое значение, оно ложно. ([[ ${aa} == "" ]] будет работать даже без кавычек, так как [[ .. ]] является особенным.)

Конечно, [ "${aa}" == "" ] проверит, что aa либо не установлено, либо пусто, как и [ -z "${aa}" ].


Чтобы проверить, что он и установлен, и пуст, мы могли бы использовать [ "${aa+x}" ] && [ -z "$aa" ] или их изящную комбинацию, украденную из ответ @Stéphane Chazelas:

if [ "${aa+x$aa}" = "x" ] ; then
    echo "aa is empty but set"
fi

(это работает, поскольку, если aa не установлено, расширение + заменяется на пустую строку, а если оно установлено, оно заменяется на x$aa, что равно x, если aa пусто.)

$ foo() { [ "${1+x$1}" = "x" ] && echo "set but empty" || echo "unset or non-empty"; }    
$ foo; foo ""; foo bar
unset or non-empty
set but empty
unset or non-empty
4
muru 28 Янв 2018 в 03:59
Спасибо. Будет ли «[[ ${aa} ==»» ]] работать даже без кавычек, поскольку [[ .. ]] является особенным», упомянутым в руководстве bash?
 – 
Tim
28 Янв 2018 в 01:36
@ Тим, да, он особенный тем, что это не обычная команда, такая как [, а часть синтаксиса оболочки. В руководстве по крайней мере упоминается, что в нем не происходит разделения слов, и он указан вместе с другими конструкциями оболочки, такими как if и (( .. )) (здесь), См. также: unix.stackexchange.com/a/32227/170373
 – 
ilkkachu
28 Янв 2018 в 01:41
1
Чтобы быть педантичным, вы могли бы сказать, что test т.е. [ требует, чтобы операторы и строки были отдельными аргументами, и что в оболочке это достигается путем помещения пробелов вокруг операторов. Поскольку, строго говоря, test никогда не видит самого пробела (оболочка удаляет его), и если кто-то достаточно извращенец, чтобы вызвать test из кода C с вызовом execve, ему не понадобится пробел.
 – 
Wildcard
28 Янв 2018 в 11:11

Ваш (попытка) тест $aa == "" эквивалентен сравнению двух пустых строк друг с другом, что приводит к истинному результату. Это связано с тем, что оболочка расширит неустановленные переменные до пустой строки. Без пробелов вокруг == проверка всегда будет истинной, поскольку она аналогична проверке двухсимвольной строки ==. Эта строка всегда верна.

Вместо этого в bash:

$ unset aa
$ if [ -v aa ]; then echo Set; else echo Not set; fi
Not set
$ aa=""
$ if [ -v aa ]; then echo Set; else echo Not set; fi
Set

Таким образом, полный тест для пустой строки будет

if [ -v aa ] && [ -z "$aa" ]; then
    echo Set but empty
fi

Из help test в bash:

-v VAR Истина, если установлена ​​переменная оболочки VAR.

Или из раздела "УСЛОВНЫЕ ВЫРАЖЕНИЯ" руководства bash:

-v varname

Истинно, если переменная оболочки varname установлена ​​(была назначена ценность).

С помощью теста -v вы проверяете имя переменной, а не ее значение.


Если вы действительно хотите сравнить строки, вы можете сделать что-то вроде

if [[ "${aa-InvalidValue}" != "InvalidValue" ]] && [ -z "$aa" ]; then
    echo Set but empty
fi

Расширение ${variable-value} будет расширено до value, если variable не установлено.

Обратите внимание, что очень похожее ${variable:-value} будет заменено на value, если variable не задано или null (пустая строка).

6
Kusalananda 28 Янв 2018 в 02:13
Спасибо. Вы знаете, почему сравнение строк через == не работает?
 – 
Tim
27 Янв 2018 в 23:15
Я просто добавил немного текста об этом.
 – 
Kusalananda
27 Янв 2018 в 23:17
Это будет работать только для оболочек с параметром -v (не sh, не POSIX).
 – 
ImHere
28 Янв 2018 в 02:01
2
Он помечен bash.
 – 
Kusalananda
28 Янв 2018 в 02:10
То, что он помечен bash, не препятствует другим возможным ответам, которые все еще действительны в bash. В любом случае то, что я сказал, это факт, а не жалоба.
 – 
ImHere
28 Янв 2018 в 02:25

Во-первых, и я полагаю, вы знаете, что == требуют пробелов вокруг него. Использование == допустимо только в bash, ksh и zsh, лучше использовать =. Кроме того, переменные, развернутые внутри теста, должны быть заключены в кавычки. Итак, я предполагаю, что строка на самом деле:

unset aa ; if [ "${aa}" = "" ]; then echo yes; else echo no; fi

При этом ваши вопросы:

Почему следующее не удается?

Потому что расширение простой неустановленной переменной идентично расширению простой переменной, установленной в ноль (из того, что может сделать тест '[').

I mean, "$aa" is the same value for both an unset aa or a null aa.

Чтобы показать, как это работает, попробуйте этот скрипт (обратите внимание, что это скрипт sh, он работает одинаково в dash, bash, ksh и/или zsh):

#!/bin/sh

blanktest(){
    if [ "${aa}" = "" ]; then echo "$1 yes"; else echo "$1 no"; fi
}

unset aa; blanktest unset
unset aa; aa="";  blanktest blank
unset aa; aa="e"; blanktest value

Что при выполнении даст:

$ ./script
unset yes
blank yes
value no

Что мне делать вместо этого?

Используйте расширение параметра под названием «Использовать альтернативное значение». :

${aa+x}

Который даст x, если переменная установлена ​​(либо null, либо значение), и null, если переменная не установлена.

Вместо этого используйте этот тест:

[ "x${aa}x" = "x${aa+x}" ] && echo yes || echo no

Сценарий тестирования (опять же, совместимый с sh):

#!/bin/sh

blanktest(){
    if [ "x${aa}x" = "x${aa+x}" ]; then echo "$1 yes"; else echo "$1 no"; fi
}

unset aa; blanktest unset
unset aa; aa="";  blanktest blank
unset aa; aa="e"; blanktest value

При выполнении напечатает это:

$ ./script
unset no
blank yes
value no

Есть несколько возможных вариантов, если вам интересно.

3
ImHere 28 Янв 2018 в 01:33