Это очень непонятный угловой случай, который можно рассматривать как ошибку в определении [
встроенного теста ; однако он соответствует поведению действительного [
двоичного файла, доступного во многих системах. Насколько я могу судить, это затрагивает только некоторые случаи , и переменную , имеющую значение , которое соответствует [
оператору , как (
, !
, =
, -e
и так далее.
Позвольте мне объяснить, почему и как обойти это в оболочках Bash и POSIX.
Объяснение:
Учтите следующее:
x="("
[ "$x" = "(" ] && echo yes || echo no
Нет проблем; вышеупомянутое не дает ошибки, и выводит yes
. Вот как мы ожидаем, что вещи будут работать. Вы можете изменить строку сравнения на, '1'
если хотите, и значение x
, и оно будет работать как положено.
Обратите внимание, что действительный /usr/bin/[
двоичный файл ведет себя так же. Если вы запустите, например, '/usr/bin/[' '(' = '(' ']'
ошибки нет, потому что программа может обнаружить, что аргументы состоят из одной операции сравнения строк.
Ошибка возникает, когда мы и со вторым выражением. Неважно, что является вторым выражением, если оно действительно. Например,
[ '1' = '1' ] && echo yes || echo no
выводит yes
, и, очевидно, является допустимым выражением; но, если мы объединим два,
[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Bash отклоняет выражение тогда и только тогда, когда x
есть (
или !
.
Если бы мы запустили вышеизложенное, используя реальную [
программу
'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no
ошибка понятна: поскольку оболочка выполняет подстановку переменных, /usr/bin/[
двоичный файл принимает только параметры (
=
(
-a
1
=
1
и завершение ]
, понятно, что он не может разобрать, начинаются ли подвыражения с открытых скобок или нет, если задействована операция and . Конечно, синтаксический анализ в виде двух строковых сравнений возможен, но жадное выполнение этого может привести к проблемам при применении к правильным выражениям с вложенными выражениями в скобках.
Проблема, действительно, заключается в том, что [
встроенная оболочка ведет себя так же, как если бы она расширила значение x
перед проверкой выражения.
(Эти неоднозначности и другие, связанные с расширением переменных, были основной причиной, по которой Bash реализовал и теперь рекомендует [[ ... ]]
вместо этого использовать тестовые выражения.)
Обходной путь тривиален и часто встречается в сценариях с использованием более старых sh
оболочек. Вы добавляете «безопасный» символ, часто x
перед строками (сравниваются оба значения), чтобы выражение распознавалось как сравнение строк:
[ "x$x" = "x(" -a "x$y" = "x1" ]
[[ "$x" = '1' && "$y" = '1' ]]