Раньше я был уверен в том, что цитирование строк - это всегда хорошая практика, чтобы избежать его разбора оболочкой.
Потом я наткнулся на это:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Попытка изолировать проблему, получая ту же ошибку:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Я решил проблему следующим образом:
[ "$x" = '1' ] && [ "$y" = '1' ]
И все же мне нужно знать, что здесь происходит.
[[ "$x" = '1' && "$y" = '1' ]]
-a
и-o
устарела по этой причине (именно это означает[OB]
верхний индекс рядом с их спецификацией). Если бы вы написали[ "$x" = 1 ] && [ "$y" = 1 ]
вместо этого, у вас все было бы хорошо, и было бы хорошо в рамках четко определенного / стандартизированного поведения.[ "x$x" = "x1" ]
для предотвращения неправильного толкования аргументов как операторов.dash
а не Bash, это все еще полезная практика.Ответы:
Это очень непонятный угловой случай, который можно рассматривать как ошибку в определении
[
встроенного теста ; однако он соответствует поведению действительного[
двоичного файла, доступного во многих системах. Насколько я могу судить, это затрагивает только некоторые случаи , и переменную , имеющую значение , которое соответствует[
оператору , как(
,!
,=
,-e
и так далее.Позвольте мне объяснить, почему и как обойти это в оболочках Bash и POSIX.
Объяснение:
Учтите следующее:
Нет проблем; вышеупомянутое не дает ошибки, и выводит
yes
. Вот как мы ожидаем, что вещи будут работать. Вы можете изменить строку сравнения на,'1'
если хотите, и значениеx
, и оно будет работать как положено.Обратите внимание, что действительный
/usr/bin/[
двоичный файл ведет себя так же. Если вы запустите, например,'/usr/bin/[' '(' = '(' ']'
ошибки нет, потому что программа может обнаружить, что аргументы состоят из одной операции сравнения строк.Ошибка возникает, когда мы и со вторым выражением. Неважно, что является вторым выражением, если оно действительно. Например,
выводит
yes
, и, очевидно, является допустимым выражением; но, если мы объединим два,Bash отклоняет выражение тогда и только тогда, когда
x
есть(
или!
.Если бы мы запустили вышеизложенное, используя реальную
[
программуошибка понятна: поскольку оболочка выполняет подстановку переменных,
/usr/bin/[
двоичный файл принимает только параметры(
=
(
-a
1
=
1
и завершение]
, понятно, что он не может разобрать, начинаются ли подвыражения с открытых скобок или нет, если задействована операция and . Конечно, синтаксический анализ в виде двух строковых сравнений возможен, но жадное выполнение этого может привести к проблемам при применении к правильным выражениям с вложенными выражениями в скобках.Проблема, действительно, заключается в том, что
[
встроенная оболочка ведет себя так же, как если бы она расширила значениеx
перед проверкой выражения.(Эти неоднозначности и другие, связанные с расширением переменных, были основной причиной, по которой Bash реализовал и теперь рекомендует
[[ ... ]]
вместо этого использовать тестовые выражения.)Обходной путь тривиален и часто встречается в сценариях с использованием более старых
sh
оболочек. Вы добавляете «безопасный» символ, частоx
перед строками (сравниваются оба значения), чтобы выражение распознавалось как сравнение строк:источник
[
встроенной ошибки. Во всяком случае, это врожденный недостаток дизайна.[[
является ключевым словом оболочки , а не просто встроенной командой, поэтому он смотрит на вещи перед удалением кавычек и фактически отменяет обычное расщепление слов. Например[[ $x == 1 ]]
, не нужно использовать"$x"
, потому что[[
контекст отличается от нормального. Во всяком случае, именно так[[
можно избежать подводных камней[
. POSIX требует[
вести себя так, как он делает, а bash в основном совместим с POSIX даже без него--posix
, поэтому переход[
на ключевое слово непривлекателен.[
.[
поведение, обремененное историей, предшествовавшей POSIX, я выбрал последнее слово.) Я лично избегаю использовать[[
в своих примерах сценарии, но только потому, что это исключение из рекомендации по цитированию, о которой я всегда говорю (поскольку пропущенные кавычки являются наиболее распространенной причиной ошибок скриптов, которые я вижу), и я не придумал простого абзаца, объясняющего, почему[[
это исключение из правил, без моих рекомендаций по цитированию подозреваемого.sh
сценариях, хорошо предшествующая стандартизации POSIX. Используя введенное подвыражения и-a
и-o
логические оператор в тестах /[
является более эффективным , что , опираясь на выражение сцепления (через&&
и||
); просто на нынешних машинах разница не имеет значения.&&
и||
, но причина в том, что нам, людям, легче поддерживать (читать, понимать и изменять, если / когда это необходимо) их, не внося ошибок. Итак, я не критикую ваше предложение, а только его обоснование. (Для очень аналогичных причин.LT.
,.GE.
и т.д. операторы сравнения в FORTRAN 77 получили гораздо более удобные для человеческой версию<
,>=
и т.д. в последующих версиях.)[
акаtest
видит:test
принимает подвыражения в скобках; поэтому он считает, что левая скобка открывает подвыражение и пытается его разобрать; синтаксический анализатор видит=
в подвыражении первое и считает, что это неявный тест на длину строки, поэтому он счастлив; за подвыражением должна следовать правильная скобка, и вместо парсера найдется1
вместо)
. И это жалуется.Когда
test
имеет ровно три аргумента, а средний аргумент является одним из распознанных операторов, он применяет этот оператор к 1-му и 3-му аргументам без поиска подвыражений в скобках.Для получения полной информации смотрите
man bash
, ищитеtest expr
.Вывод: используемый алгоритм разбора
test
сложен. Используйте только простые выражения и используйте оболочку операторов!
,&&
и||
объединить их.источник