Кілька логічних операторів, ((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 

Якщо всі ваші тести чисельні, є ще один спосіб, який розмежовує архметичні вирази . Арифметичні вирази виконують обчислення цілих чисел із синтаксисом, подібним С.

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.
Жил "ТАК - перестань бути злим"

@Giles - спасибі Баш 2, мабуть, сміється більшості. Це пов'язано з нашим управлінням та небажанням відмовлятися від старих платформ. Bash 2 і GCC 3 з'являються на нашому тестуванні Fedora 1. Ми відпустимо старіші платформи з нагоди, але для цього повинні бути причини. Ми не підписуємось на політику Apple, Microsoft, Mozilla тощо.

@jww Будь ласка, розумійте, що пріоритет ||і &&перехід від і [до . Рівний пріоритет у (використовуйте круглі дужки для контролю порядку оцінювання), але && має вищий пріоритет у та ніж (як у З). [[(([[[((||

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

@Kusalananda - Дякуємо за те, що ви працюєте з портативністю. Сценарій повинен працювати в системах з Bash 2 через Bash 4. Чи [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]будуть проблеми з ними?

Не буде жодних проблем, якщо ви дотримуєтесь bashбудь-якої іншої оболонки, яка розбирається [[ ... ]].
Kusalananda
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.