Це дуже незрозумілий кутовий випадок, що можна розглянути помилку в тому, як визначено [
вбудований тест ; однак це відповідає поведінці фактичних [
бінарних файлів, доступних у багатьох системах. Наскільки я можу судити, це зачіпає тільки деякі випадки , і змінну , що має значення , яке відповідає [
оператору , як (
, !
, =
, -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
та закінчення ]
, вона, зрозуміло, не може розібратися, чи відкриті дужки починають підвираз, чи ні, там задіяна і операція. Зрозуміло, можливий аналіз цього порівняння як двох рядкових порівнянь, але робити це так жадібно, що може спричинити проблеми при застосуванні до правильних виразів із скобками в дужках.
Справді, проблема полягає в тому, що [
вбудована оболонка поводиться так само, як ніби вона розширює значення, x
перш ніж вивчати вираз.
(Ці двозначності та інші, пов’язані з розширенням змінної, були великою причиною того, чому Bash реалізував і тепер рекомендує [[ ... ]]
замість цього використовувати тестові вирази.)
Вирішення проблеми тривіальне, і часто його можна побачити у сценаріях із використанням старих sh
оболонок. Ви додаєте "безпечний" символ часто x
перед рядками (обидва значення порівнюються), щоб переконання виразу було визнано порівнянням рядків:
[ "x$x" = "x(" -a "x$y" = "x1" ]
[[ "$x" = '1' && "$y" = '1' ]]