Jaka jest składnia złożonego warunku w powłoce?

11

Jeśli chcesz wyrazić następujący test w powłoce (sh):

if ( a == 1 && ( b == 1 || b == 2 )) { ... }

Do tej pory najlepsze, co udało mi się napisać, to:

if [[ $a -eq 1 ]]; then
  if [[ $b -eq 1 || $b -eq 2 ]]; then
     ...
  fi
fi

Nie wiem jak połączyć && i || z właściwym pierwszeństwem. Googling nie dał mi żadnej odpowiedzi (tutoriale podają tylko podstawowe przykłady, jeśli takie istnieją)

Jaka jest składnia, aby połączyć te dwa, jeśli w jeden?

Offirmo
źródło
1
Czy jest jakiś powód, aby nie używać ksh? Wówczas początkowy kod wymagałby tylko jednej pary nawiasów:if (( a == 1 && ( b == 1 || b == 2 ))); then :; fi
manatwork

Odpowiedzi:

15

Pamiętaj, że [[ ]]nie ma go w Bourne ani POSIX sh. Aby uzyskać prawdziwą shskładnię, można to zrobić na kilka sposobów.

Używanie tylko jednej [ ]pary

if [ 1 -eq "$a" -a \( 1 -eq "$b" -o 2 -eq "$b" \) ]; then
    # ...
fi

lub Unikanie POSIX -ai -oopcji 1

if [ 1 -eq "$a" ] && { [ 1 -eq "$b" ] || [ 2 -eq "$b" ]; }; then
    # ...
fi

1 Jednym z powodów, dla uniknięcia -ai -ojest maksymalna mobilność - nie wszystkie testlub [implementacje mogą obsługiwać więcej niż 4 argumenty, co jest dokładnie to, co masz, jeśli łańcuch wyrażeń z -ai -oa \( \).

jw013
źródło
Moje skrypty mają #! / bin / sh shebang, a domyślną powłoką jest ksh . Dlaczego nie byłby używany?
Offirmo,
2
@Offirmo Jeśli zadeklarujesz swój skrypt jako /bin/sh, pisanie go przy użyciu funkcji, których nie ma, jest nieprawidłowe sh. Powinieneś zdawać sobie sprawę, że kshto NIE jest sh, ale raczej nadzbiór sh. Jeśli zabierzesz swój skrypt, kshale zadeklarowany jako, shdo systemu, w którym shnie ma dowiązania symbolicznego ksh, może się on zepsuć. Możesz uniknąć tego problemu, upewniając się, że skrypt pasuje do deklaracji.
jw013,
Myślałem, że ustawienie / bin / sh jako shebang spowoduje, że moje skrypty będą wywoływane przez / bin / sh . Czy masz na myśli to, że moja domyślna powłoka (ksh) ignoruje shebang i interpretuje go bezpośrednio?
Offirmo,
@Offirmo Masz rację: szereg /bin/shwywoła /bin/sh. Problem /bin/shnie jest akceptowany [[. Nie możesz użyć [[ani użyć powłoki, która akceptuje [[, np. kshJako twój shebang.
jw013
2
Problemem nie jest to, że -a/ -onie są obsługiwane przez niektóre implementacje, ale to, że nie jest wiarygodne w przypadku arbitralnych argumentów. Podobnie [ "$a" = "$b" -o "$b" = "$c" ]mogłoby się nie powieść dla wartości $apodobnych !przy wielu [implementacjach.
Stéphane Chazelas
2

Wypróbowałem trochę składni i znalazłem to

if [[ $a -eq 1 ]] && [[ $b -eq 1 || $b -eq 2 ]]; then
 ...
fi

działa.

Offirmo
źródło
1
działa to dla wersji bash powyżej 4.
Nikhil Mulley
2

Inne podejście POSIX:

if [ "$((a == 1 && ( b == 1 || b == 2 )))" -ne 0 ]; then
  ...
fi
Stéphane Chazelas
źródło
0

Właściwy sposób to zrobić za pomocą samego shell:

if test 1 -eq "$a" && test 1 -eq "$b" -o 2 -eq "$b"; then 
    ...
fi

Wyjaśnienie:

testtutaj służy jako nawias i -ologiczny lub. Jeśli logiczny i wewnątrz „nawiasu” użyłbyś -a.

Więcej na ten temat można znaleźć tutaj

Omer Dagan
źródło