Несколько логических операторов, ((A || B) && C) и «синтаксическая ошибка рядом с неожиданным токеном»

24

Я работаю с Bash 3, и я пытаюсь сформировать условия. В C / C ++, его мертвым просто: ((A || B) && C). В Bash все оказалось не так (я думаю, что авторы Git, должно быть, внесли этот код, прежде чем перейти к другим попыткам).

Это не работает. Обратите внимание, что <0 or 1>это не строковый литерал; это означает 0 или 1 (как правило, происходит от grep -i).

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

Это приводит к:

line 322: syntax error near unexpected token `[['

Я тогда попробовал:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

это приводит к:

line 322: syntax error near unexpected token `[['

Частично проблема заключается в том, что результаты поиска представляют собой тривиальные примеры, а не более сложные примеры с составными условиями.

Как мне выполнить простую ((A || B) && C)в Bash?


Я готов просто развернуть его и повторить те же команды в нескольких блоках:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

источник

Ответы:

42

Синтаксис bash не является C-подобным, даже если небольшая его часть основана на C. Вы не можете просто попытаться написать C-код и ожидать, что он будет работать.

Главное в оболочке - запускать команды. Команда открытой скобки [- это команда, которая выполняет один тест¹. Вы даже можете написать это как test(без заключительной закрывающей скобки). Операторы ||и &&являются операторами оболочки, они объединяют команды , а не тесты.

Поэтому, когда вы пишете

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

это анализируется как

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

который так же, как

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

Обратите внимание на несбалансированные скобки? Да, это не хорошо. Ваша попытка с круглыми скобками имеет ту же проблему: ложные скобки.

Синтаксис для группирования команд - это фигурные скобки. Для анализа фигурных скобок перед ними требуется полная команда, поэтому вам нужно завершить команду внутри фигурных скобок с помощью новой строки или точки с запятой.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

Есть альтернативный способ, который заключается в использовании двойных скобок. В отличие от одинарных скобок, двойные скобки имеют специальный синтаксис оболочки. Они разграничивают условные выражения . Внутри двойных скобок вы можете использовать скобки и операторы, такие как &&и ||. Поскольку двойные скобки являются синтаксисом оболочки, оболочка знает, что когда эти операторы находятся внутри скобок, они являются частью синтаксиса условного выражения, а не частью обычного синтаксиса команды оболочки.

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

Если все ваши тесты являются числовыми, есть еще один способ, который разграничивает выражения artihmetic . Арифметические выражения выполняют целочисленные вычисления с очень C-подобным синтаксисом.

if (((A == 0 || B != 0) && C == 0)); then 

Вы можете найти мой учебник для начинающих скобки полезным.

[может быть использован в простой ш. [[и ((специфичны для bash (и ksh и zsh).

¹ Он также может комбинировать несколько тестов с булевыми операторами, но это громоздко для использования и имеет тонкие ловушки, поэтому я не буду объяснять это.

Жиль "ТАК - перестань быть злым"
источник
Спасибо Джайлз. Это относится к Bash 2 через Bash 4? Мне нужно что-то мягкое переносимость, но Кусалананда упомянул, что некоторые вещи, которыми я не занимался, переносимы (это означает, что то, что я делаю, не переносимо?). Здесь мягко означает Bash 2 до Bash 4.
@jww [[ … ]]был добавлен в bash 2.02. ((…))был добавлен в Bash 2.0.
Жиль "ТАК - перестань быть злым"
@ Джайлс - спасибо. Bash 2, вероятно, смехотворен для большинства. Это связано с нашим управлением и нежеланием отказываться от старых платформ. Bash 2 и GCC 3 появляются на нашем тестировании Fedora 1. Мы позволим более старой платформе работать, но для этого должны быть причины. Мы не подписываемся на политику отказа от программного обеспечения Apple, Microsoft, Mozilla и т. Д.
@jww Пожалуйста, поймите, что приоритет ||и &&изменить с [на [[и ((. Равный приоритет в [(используйте круглые скобки для управления порядком оценки), но && имеет более высокий приоритет [[и ((чем ||(как в C).
6

Используйте [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ...

Если вы предпочитаете [, следующие работы:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...
Стивен Китт
источник
3

Используйте -oоператор вместо вашего вложенного ||. Вы также можете использовать -aдля замены &&, если это необходимо в других ваших утверждениях.

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi
Захари Брейди
источник
Спасибо. Я сталкивался с -aи -o, но я не экспериментировал с ними. Кто-то на Stack Overflow сказал, чтобы избежать этого. Я в основном согласен с ними, поскольку сценарий менее читабелен при их использовании
... но более портативный.
Кусалананда
@Kusalananda - Спасибо за помощь в переносимости. Скрипт должен работать в системах с Bash 2 до Bash 4. Будут ли [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]проблемы с ними?
Там не будет никаких проблем, пока вы держите bashили любой другой оболочки, которая понимает [[ ... ]].
Кусалананда