Jak reprezentować wiele warunków w instrukcji if w powłoce?

307

Chcę reprezentować wiele takich warunków:

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

ale kiedy wykonuję skrypt, pokazuje

syntax error at line 15: `[' unexpected, 

gdzie wiersz 15 pokazuje, czy ...

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

użytkownik389955
źródło
6
Nie pytasz o warunki powłoki, ale warunki testowe . Całe wyrażenie w twoim przykładzie jest oceniane przez test( [), a nie przez powłokę. Powłoka ocenia tylko status wyjścia [.
ceving
Powiązane:
Złóż

Odpowiedzi:

381

Klasyczna technika (metaznaki ucieczki):

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

Odwołuję się do $gpodwójnych cudzysłowów; to ogólnie dobra praktyka. Ściśle, nawiasy nie są potrzebne, ponieważ pierwszeństwo -ai -osprawia, że nawet bez nich poprawić.

Zauważ, że operatory -ai -osą częścią specyfikacji POSIX test, aka [, głównie dla kompatybilności wstecznej (ponieważ na przykład były częścią testwersji 7.IX UNIX), ale są wyraźnie oznaczone przez POSIX jako „przestarzałe”. Bash (zobacz wyrażeń warunkowych ) wydaje się wywłaszczyć klasyczne i POSIX znaczenia dla -ai -oz własnych operatorów alternatywnych, które przyjmują argumenty.


Z pewną ostrożnością możesz użyć bardziej nowoczesnego [[operatora, ale pamiętaj, ż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 uruchomienia przy użyciu Bash 3.2.57 na 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 musisz cytować zmiennych, [[jak to robisz, [ponieważ nie jest to osobne polecenie w taki sam sposób [.


Czy to nie jest klasyczne pytanie?

Tak bym pomyślał. Istnieje jednak inna alternatywa, a 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 przeczytasz wytyczne „przenośnej powłoki” dla autoconfnarzędzia lub powiązanych pakietów, ta notacja - używanie „ ||” i „ &&” - jest tym, co zalecają. Przypuszczam, że mógłbyś nawet posunąć się tak daleko:

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, który ma być powtarzany, składa się z wielu wierszy, powtórzenie jest zbyt bolesne i preferowana jest jedna z wcześniejszych wersji - lub musisz zawinąć akcje w funkcję wywoływaną w różnych thenblokach.

Jonathan Leffler
źródło
9
Dobrze wiedzieć, że nawiasy, które uciekły, działają; Tak na marginesie: w tym konkretnym przypadku, nawiasy nie są nawet potrzebne, ponieważ -afaktycznie ma wyższy priorytet niż -o(w przeciwieństwie do &&i ||w powłoce - jednak wewnątrz bash [[ ... ]] warunkowych , && także ma wyższy priorytet niż ||). Jeśli chcesz uniknąć, -aa -odla maksymalnej niezawodności i przenośności - co sugeruje sama strona podręcznika POSIX - możesz również użyć podpowłoki do grupowania:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0
Dobre rozwiązanie, ale podoba mi się forma bez nawiasów i nawiasów:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK
Trochę się spóźniłem, ale nadal jest to najlepszy wynik wyszukiwania, więc chciałbym zauważyć, że użycie &&lub ||jest również preferowane, ponieważ bardziej przypomina warunki warunkowe w innych językach i pozwala na warunki zwarcia. Na przykład: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Robiąc to w ten sposób, jeśli check_inodesjest pusty, unikasz dwóch wywołań stat, podczas gdy większy, złożony warunek testowy musi przetworzyć wszystkie argumenty przed wykonaniem (co może również prowadzić do błędów, jeśli zapomnisz o tym zachowaniu).
Haravikk
182

W Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]
Wstrzymano do odwołania.
źródło
9
Zdecydowanie najlepsze podejście w bash. Nawiasem mówiąc: w tym konkretnym przypadku nawiasy nie są nawet potrzebne, ponieważ wewnętrzne [[ ... ]] warunki warunkowe && faktycznie mają wyższy priorytet niż ||- w przeciwieństwie do zewnętrznych warunków warunkowych.
mklement0
Czy istnieje sposób, aby dowiedzieć się, który warunek jest tutaj spełniony? Czy to był ten w pierwszym nawiasie, czy drugi?
Firelord,
1
@Firelord: Musisz rozdzielić warunki na instrukcję if/ i umieścić elsekod pomiędzy theni fiwstawić funkcję, aby uniknąć jej powtórzenia. W bardzo prostych przypadkach można użyć caseinstrukcji z ;&przewrotem (w Bash 4).
Wstrzymano do odwołania.
1
Jaki jest cel dwóch nawiasów kwadratowych?
peterchaula,
2
@peter: Proszę zobaczyć moją odpowiedź tutaj , Konstrukcje warunkowe w Bash Manual i BashFAQ / 31 .
Wstrzymano do odwołania.
37

Działa /bin/bashnastępujące działanie:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi
sunitha
źródło
8

Uważaj, jeśli masz spacje w zmiennych łańcuchowych i sprawdzasz, czy istnieją. Pamiętaj o ich prawidłowym zacytowaniu.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then
orkoden
źródło
1
kiedy używamy nawiasów klamrowych po symbolu dolara !!
YouAreAwesome
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
ghostdog74
źródło
2
To bezużyteczna podpowłoka. { ;}można zamiast tego użyć.
phk
2

W bash do porównywania ciągów możesz użyć następującej techniki.

if [ $var OP "val" ]; then
    echo "statements"
fi

Przykład:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi
Ashan Priyadarshana
źródło
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
Debjyoti Banerjee
źródło
3
Witamy w Stack Overflow! To pytanie szuka wyjaśnienia , a nie tylko działającego kodu. Twoja odpowiedź nie zapewnia wglądu pytającemu i może zostać usunięta. Proszę edit wyjaśnić, co powoduje, że obserwowane objawy.
Toby Speight
0

możesz też połączyć więcej niż 2 warunki:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
Yordan Georgiev
źródło