Wiele operatorów logicznych ((A || B) i&C) oraz „błąd składni w pobliżu nieoczekiwanego tokena”

24

Pracuję z Bash 3 i próbuję utworzyć warunek. W C / C ++, jego martwy prosta ((A || B) && C). W Bash okazało się, że tak nie jest (myślę, że autorzy Git musieli napisać ten kod, zanim przystąpili do innych przedsięwzięć).

To nie działa. Zauważ, że <0 or 1>nie jest to dosłowny ciąg; oznacza 0 lub 1 (zazwyczaj pochodzi od 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

Powoduje to:

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

Potem spróbowałem:

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

skutkuje:

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

Częścią problemu jest to, że wyniki wyszukiwania są trywialnymi przykładami, a nie bardziej złożonymi przykładami ze złożonymi warunkami warunkowymi.

Jak wykonać proste ((A || B) && C)w Bash?


Jestem gotowy, aby go rozwinąć i powtórzyć te same polecenia w wielu blokach:

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

źródło

Odpowiedzi:

42

Składnia bash nie jest podobna do C, nawet jeśli jego niewielka część jest zainspirowana C. Nie możesz po prostu spróbować napisać kodu C i oczekiwać, że zadziała.

Głównym celem powłoki jest uruchamianie poleceń. Polecenie open-bracket [to polecenie, które wykonuje pojedynczy test¹. Możesz nawet napisać to jako test(bez końcowego nawiasu zamykającego). ||I &&operatorzy operatorzy powłoki, łączą poleceń , nie testuje.

Więc kiedy piszesz

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

to jest parsowane jako

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

który jest taki sam jak

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

Czy zauważyłeś niezrównoważone nawiasy? Tak, to nie jest dobre. Twoja próba z nawiasami ma ten sam problem: fałszywe nawiasy.

Składnia grupowania poleceń razem to nawiasy klamrowe. Sposób, w jaki parsowane są nawiasy klamrowe, wymaga pełnego polecenia przed nimi, dlatego należy zakończyć polecenie wewnątrz nawiasów klamrowych lub średnika.

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

Alternatywnym sposobem jest użycie podwójnych nawiasów. W przeciwieństwie do pojedynczych nawiasów, podwójne nawiasy są specjalną składnią powłoki. Ograniczają wyrażenia warunkowe . W podwójnych nawiasach możesz używać nawiasów i operatorów, takich jak &&i ||. Ponieważ podwójne nawiasy klamrowe są składnią powłoki, powłoka wie, że kiedy te operatory znajdują się w nawiasach, są one częścią składni wyrażeń warunkowych, a nie częścią zwykłej składni poleceń powłoki.

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

Jeśli wszystkie twoje testy są numeryczne, istnieje jeszcze inny sposób, który ogranicza wyrażenia artihmetyczne . Wyrażenia arytmetyczne wykonują obliczenia na liczbach całkowitych z bardzo podobną do C składnią.

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

Może się przydać mój podkład do wspornika bash .

[może być używany w zwykłym sh. [[i ((są specyficzne dla bash (oraz ksh i zsh).

¹ Może również łączyć wiele testów z operatorami logicznymi, ale jest to uciążliwe w użyciu i ma subtelne pułapki, więc nie wyjaśnię tego.

Gilles „SO- przestań być zły”
źródło
Dzięki Giles. Czy dotyczy to wersji Bash 2 do Bash 4? Potrzebuję czegoś delikatnie przenośnego, ale Kusalananda wspomniał, że niektóre rzeczy, których nie robiłem, są przenośne (czy to oznacza, że ​​to, co robię, nie jest przenośne?). Tutaj delikatnie oznacza Bash 2 do Bash 4.
@jww [[ … ]]został dodany w bash 2.02. ((…))został dodany w bash 2.0.
Gilles „SO- przestań być zły”
@Giles - dzięki. Bash 2 jest prawdopodobnie śmieszny. Wynika to z naszego zarządzania i niechęci do porzucania starszych platform. Bash 2 i GCC 3 pojawiają się w naszych testach Fedory 1. Od czasu do czasu wypuszczamy starszą platformę, ale muszą być ku temu powody. Nie subskrybujemy zasad Apple, Microsoft, Mozilla itp. Dotyczących porzucania oprogramowania.
@jww Proszę zrozumieć, że pierwszeństwo ||i &&zmiana z [na [[i ((. Równy priorytet w [(użyj nawiasów, aby kontrolować kolejność oceny), ale && ma wyższy priorytet w [[i ((niż ||(jak w C).
6

Użyj [[:

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

Jeśli wolisz [, następujące działania:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...
Stephen Kitt
źródło
3

Użyj -ooperatora zamiast zagnieżdżonego ||. Możesz również użyć -ado zastąpienia &&, jeśli to konieczne w innych wyciągach.

   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
Zachary Brady
źródło
Dzięki. Natknąłem się na -ai -o, ale nie eksperymentowałem z nimi. Ktoś na stosie przepełnienia powiedział, aby tego uniknąć. W większości się z nimi zgadzałem, ponieważ użycie skryptu jest mniej czytelne.
... ale bardziej przenośny.
Kusalananda
@Kusalananda - Dzięki za informacje dotyczące przenośności. Skrypt musi działać na systemach z wersją Bash 2 do Bash 4. Czy [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]będzie z nimi problem?
Nie będzie problemów, dopóki trzymasz się bashpowłoki lub innej powłoki, która to rozumie [[ ... ]].
Kusalananda