2010-09-29 15 views
194

chcę reprezentować wiele warunków, takich jak ten:Jak reprezentować wiele warunków w powłoce, jeśli instrukcja?

if [ ($g -eq 1 -a "$c" = "123") -o ($g -eq 2 -a "$c" = "456") ] 
then 
    echo abc; 
else 
    echo efg; 
fi 

ale kiedy wykonać skrypt, pokazuje

syntax error at line 15: `[' unexpected, 

gdzie linia 15 jest jednym pokazując jeśli ....

Co jest nie tak z tym warunkiem? Chyba coś jest nie tak z ().

+4

Nie proszą o warunkach powłoki ale [test] (https://de.wikipedia.org/wiki/Test_%28Unix % 29) warunki. Całe wyrażenie w twoim przykładzie jest oceniane przez 'test' (' ['), a nie przez powłokę. Powłoka ocenia jedynie status wyjścia '[. – ceving

+0

Zobacz także http://stackoverflow.com/questions/16203088/multiple-conditions-if-statement-bash-script – tripleee

Odpowiedz

252

techniką klasyczną (ucieczki metaznaki):

if [ \("$g" -eq 1 -a "$c" = "123" \) -o \("$g" -eq 2 -a "$c" = "456" \) ] 
then echo abc 
else echo efg 
fi 

mam zamknięte odniesień do $g w cudzysłów; to ogólnie dobra praktyka. Ściśle rzecz ujmując, nawiasy nie są potrzebne, ponieważ pierwszeństwo daje im poprawność nawet bez nich.

Zauważ, że operatorzy -a i -o są częścią specyfikacji POSIX dla test, aka [, głównie dla kompatybilności wstecznej (ponieważ były one częścią test w 7th Edition UNIX, na przykład), ale są one wyraźnie oznaczone jako "przestarzałe" według POSIX. Bash (patrz conditional expressions) wydaje się wywłaszać znaczenie klasyczne i POSIX dla -a i -o z własnymi alternatywnymi operatorami, które pobierają argumenty.


Z pewną ostrożnością, można użyć bardziej nowoczesny operator [[, ale należy pamiętać, że wersje w bash i Korn Shell (na przykład) nie muszą być identyczne.

for g in 1 2 3 
do 
    for c in 123 456 789 
    do 
     if [[ ("$g" -eq 1 && "$c" = "123") || ("$g" -eq 2 && "$c" = "456") ]] 
     then echo "g = $g; c = $c; true" 
     else echo "g = $g; c = $c; false" 
     fi 
    done 
done 

Przykład prowadzony przy użyciu Bash 3.2.57 w systemie Mac OS X:

g = 1; c = 123; true 
g = 1; c = 456; false 
g = 1; c = 789; false 
g = 2; c = 123; false 
g = 2; c = 456; true 
g = 2; c = 789; false 
g = 3; c = 123; false 
g = 3; c = 456; false 
g = 3; c = 789; false 

Nie trzeba zacytować zmienne w [[ jak to zrobić z [, ponieważ nie jest odrębną polecenia w taki sam sposób, jak [.


Czy nie jest to klasyczne pytanie?

Tak bym pomyślał. Jednakże, nie ma innej alternatywy, mianowicie:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ] 
then echo abc 
else echo efg 
fi 

Rzeczywiście, jeśli czytasz wytycznymi "przenośny Shell dla narzędzia autoconf lub z nimi związanych, w tym notacji - używając„||”oraz„&&”- to co oni polecić. Przypuszczam, że możesz nawet posunąć się do tego, że:

if [ "$g" -eq 1 ] && [ "$c" = "123" ] 
then echo abc 
elif [ "$g" -eq 2 ] && [ "$c" = "456" ] 
then echo abc 
else echo efg 
fi 

Tam, gdzie działania są tak trywialne jak echo, nie jest to złe. Gdy blok akcji do powtórzenia jest wielokrotny, powtórzenie jest zbyt bolesne i preferowana jest jedna z wcześniejszych wersji - lub musisz zawinąć akcje w funkcję, która jest wywoływana w różnych blokach then.

+0

Wielkie dzięki, \\ (działa, dlaczego nie znalazłem odpowiedzi z innej strony internetowej? To jest klasyczne pytanie? – user389955

+6

Dobrze wiedzieć, że uniknęły nawiasów, a na marginesie: w tym przypadku nawiasy nie są nawet potrzebne, ponieważ '-a' ma wyższy priorytet niż' -o' (w przeciwieństwie do '&&' i '||' w powłoce - jednak _inside_ 'bash'' [[...]] '_conditionals_,' && '_also_ ma wyższy priorytet niż' || ') .Jeśli chcesz uniknąć' - a' i '-o' dla maksymalnej niezawodności i przenośności - co sugeruje [sama strona podręcznika POSIX] (http://man.cx/test) - możesz również użyć _subshells_ do grupowania:' if ([$ g -eq 1] && ["$ c" = "123"]) || ([$ g -eq 2] && ["$ c" = "456"]) ' – mklement0

120

w bash:

if [[ ($g == 1 && $c == 123) || ($g == 2 && $c == 456) ]] 
+7

Zdecydowanie najlepsze podejście w' bash'. : w tym przypadku nawiasy nie są nawet potrzebne, ponieważ _inside_ '[[...]]' _conditionals_ '&&' faktycznie ma wyższy priorytet niż '||' - w odróżnieniu od _outside_ takich warunkowych. – mklement0

+0

Czy istnieje sposób, aby dowiedzieć się, który stan się zgadza? Czy to był ten w pierwszym nawiasie, czy drugi? – Firelord

+1

@Firelord: Będziesz musiał oddzielić warunki do instrukcji "if"/"else" i mieć kod między 'then' i' fi' wstawić funkcję, aby uniknąć jej powtarzania. W bardzo prostych przypadkach można użyć instrukcji 'case' z'; & 'fallthrough (w Bash 4). –

6
$ g=3 
$ c=133 
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg" 
efg 
$ g=1 
$ c=123 
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg" 
abc 
+1

To bezużyteczne podpowłodzenie. Zamiast tego można użyć '{;}'. – phk

5

Bądź ostrożny, jeśli masz spacji w zmiennych łańcuchowych i sprawdzeniu istnienia. Pamiętaj, aby zacytować je poprawnie.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then 
+1

kiedy używamy nawiasów klamrowych po symbolu dolara !! –

12

Korzystanie /bin/bash dodaje zadziała:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then 
    echo "Entered $option" 
fi 
0
#!/bin/bash 

current_usage=$(df -h | grep 'gfsvg-gfslv' | awk {'print $5'}) 
echo $current_usage 
critical_usage=6% 
warning_usage=3% 

if [ ${current_usage%?} -lt ${warning_usage%?} ]; then 
echo OK current usage is $current_usage 
elif [ ${current_usage%?} -ge ${warning_usage%?} ] && [ ${current_usage%?} -lt ${critical_usage%?} ]; then 
echo Warning $current_usage 
else 
echo Critical $current_usage 
fi 
+3

Witamy w Stack Overflow! To pytanie szuka * wyjaśnienia *, a nie tylko kodu roboczego. Twoja odpowiedź nie zapewnia wglądu dla pytającego i może zostać usunięta. Proszę [edytuj] wyjaśnić, co powoduje obserwowane objawy. –