Есть такой сабреддит — /r/nononoyes, где публикуют видео, в которых происходит что-то такое, что, на первый взгляд, кажется ужасно неправильным, идущим к катастрофе. Но в конце всё, чудесным образом, заканчивается хорошо.
В том сабреддите хорошо смотрелась бы команда [ -z $var ]
.
Это — конструкция, которая используется в bash для проверки того, является ли переменная пустой. Но тут не хватает кавычек (её более правильный вариант выглядел бы как [ -z "$var" ]
). Это, при работе с переменными, которые могут быть пустыми, часто приводит к неприятностям.
Рассмотрим обратное выражение — [ -n $var ]
, которое проверяет переменную на то, что она является непустой. Та же проблема с кавычками делает её полностью бесполезной:
Входные данные | Ожидаемый результат | [ -n $var ] |
|
|
|
|
|
|
|
|
|
Причиной возникновения этих проблем является комбинация разделения слов и того факта, что открывающая квадратная скобка, [
, это не часть синтаксической конструкции командной оболочки, а всего лишь внешняя команда с необычным именем. Я уже об этом писал.
Нахождение результатов работы команды [
зависит от количества аргументов. Значения аргументов гораздо слабее влияют на результат. Вот упрощённая выдержка из описания POSIX-команды test, составляя которую, я не обращал внимания на отрицание:
Количество аргументов | Действие | Типичный пример |
0 | Возврат False . |
|
1 | Возврат True , если аргумент $1 не является пустым. |
|
2 | Применение унарного оператора $1 к $2 . |
|
3 | Применение бинарного оператора $2 к $1 и к $3 . |
|
Если это учесть — становится понятным то, почему команда [ -n $var ]
в двух случаях ведёт себя неправильно:
- Когда переменная является пустой и при этом не заключена в кавычки, она удаляется, и мы передаём команде 1 аргумент — литеральную строку
-n
. Так как-n
не является пустой строкой — команда выдаётTrue
, а должна была бы выдатьFalse
. - Когда переменная содержит
foo bar
и не заключена в кавычки, она разделяется на два аргумента, в результате мы передаём команде 3 аргумента:-n
,foo
иbar
. Так какfoo
— это не бинарный оператор, результатом работы команды являетсяFalse
(с выдачей сообщения об ошибке), а, на самом деле, результатом должно бытьTrue
.
А теперь посмотрим на то, как работает [ -z $var ]
:
Входные данные | Ожидаемый результат | [ -z $var ] | Команда test |
|
True : пустая переменная |
|
1 аргумент: проверка того, является ли -z непустой переменной |
|
False : непустая переменная |
|
2 аргумента: применение -z к foo |
|
False : непустая переменная |
False (ошибка) |
3 аргумента: применение foo к -z и bar |
Эта команда выполняет совершенно неправильные и неожиданные действия, обрабатывая пустые строки и те случаи, которые расцениваются как передача нескольких аргументов. Но в обоих случаях ошибки приводят к получению правильных результатов.
Другими словами, [ -z $var ]
работает гораздо лучше, чем этого можно ожидать.
Конечно, это не значит, что я призываю всех отказаться от кавычек. Для "foo bar"
[ -z $var ]
вернёт правильный код выхода, но при этом выведет некрасивое сообщение об ошибке. Для ” ”
(это — строка, в которой имеются только пробелы) вернёт True
, хотя должна вернуть False
, так как соответствующий аргумент удаляется так, как если бы он был пустым. Bash, кроме того, что неправильно, при попытке воспользоваться механизмом внедрения кода, пропустит конструкцию var="foo -o x"
, так как она будет нормально воспринята командой test
.
Какова мораль сей басни? Она такая же, как и всегда: не забывайте о кавычках. Даже тогда, когда кажется, что и без них всё работает как надо.
Утилита ShellCheck знает об этих особенностях. Тут можно проверить код, о котором мы говорили. А именно, анализируя конструкцию [ -n $var ]
, программа разразится гневным сообщением красного цвета, а рассматривая конструкцию [ -z $var ]
— всего лишь покажет обычное зелёное предупреждение об отсутствии кавычек.
Сталкивались ли, при работе в bash, с проблемами, связанными с кавычками?
Автор: ru_vds