Proste operatory logiczne w Bash


256

Mam kilka zmiennych i chcę sprawdzić następujący warunek (zapisany słowami, a następnie moja nieudana próba skryptu bash):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

W mojej nieudanej próbie wymyśliłem:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi

Odpowiedzi:


682

To, co napisałeś, prawie działa (działałoby, gdyby wszystkie zmienne były liczbami), ale wcale nie jest to idiomatyczne.

  • (…)nawiasy oznaczają podpowłokę . To, co jest w nich, nie jest wyrażeniem jak w wielu innych językach. To lista poleceń (podobnie jak poza nawiasami). Te polecenia są wykonywane w osobnym podprocesie, więc żadne przekierowanie, przypisanie itp. Wykonane w nawiasach nie ma wpływu poza nawiasami.
    • Z wiodącym znakiem dolara $(…)jest podstawienie polecenia : w nawiasach znajduje się polecenie, a dane wyjściowe polecenia są używane jako część wiersza polecenia (po dodatkowych rozwinięciach, chyba że podstawienie występuje między podwójnymi cudzysłowami, ale to inna historia ) .
  • { … }nawiasy klamrowe są jak nawiasy, ponieważ grupują polecenia, ale wpływają tylko na parsowanie, a nie na grupowanie. Program x=2; { x=4; }; echo $xdrukuje 4, podczas gdy x=2; (x=4); echo $xdrukuje 2. (Nawiasy klamrowe wymagają spacji wokół nich i średnika przed zamknięciem, podczas gdy nawiasy nie. To tylko dziwactwo składniowe.)
    • Z wiodącym znakiem dolara, ${VAR}jest rozszerzeniem parametru , rozwijającym się do wartości zmiennej, z możliwymi dodatkowymi przekształceniami.
  • ((…))podwójne nawiasy otaczają instrukcję arytmetyczną , czyli obliczanie liczb całkowitych, ze składnią podobną do innych języków programowania. Ta składnia jest najczęściej używana do przypisań i warunków.
    • Ta sama składnia jest używana w wyrażeniach arytmetycznych $((…)), które rozwijają się do wartości całkowitej wyrażenia.
  • [[ … ]]podwójne nawiasy otaczają wyrażenia warunkowe . Wyrażenia warunkowe są w większości oparte na operatorach, takich jak -n $variabletestowanie, czy zmienna jest pusta i -e $filetestowanie, czy plik istnieje. Istnieją również operatory równości łańcuchów: "$string1" == "$string2"(uważaj, że prawa strona jest wzorcem, np. [[ $foo == a* ]]Testy, jeśli $foozaczyna się od, aa [[ $foo == "a*" ]]testy, jeśli $foojest dokładnie a*), i znane !, &&i ||operatory dla negacji, koniunkcji i rozłączania, a także nawiasy do grupowania. Należy pamiętać, że trzeba przestrzeń wokół każdego operatora (np [[ "$x" == "$y" ]], nie [[ "$x"=="$y" ]]) i spacji lub znaku podobnego ;wewnątrz i na zewnątrz uchwytów (np [[ -n $foo ]], nie[[-n $foo]]).
  • [ … ]pojedyncze nawiasy kwadratowe są alternatywną formą wyrażeń warunkowych z większą liczbą dziwactw (ale starszych i bardziej przenośnych). Nie pisz na razie żadnych; zacznij się o nich martwić, gdy znajdziesz skrypty, które je zawierają.

Oto idiomatyczny sposób napisania testu w bash:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Jeśli potrzebujesz przenośności do innych powłok, byłby to sposób (zwróć uwagę na dodatkowe cytowanie i oddzielne zestawy nawiasów wokół każdego pojedynczego testu oraz użycie tradycyjnego =operatora zamiast ==wariantu ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then

31
Świetny post, podsumowanie w nawiasach jest po prostu idealne.
KomodoDave

10
Lepiej jest użyć ==do odróżnienia porównania od przypisania zmiennej (która również jest =)
Will Sheppard,

1
Och, miałem na myśli pojedyncze (okrągłe) nawiasy, przepraszam za zamieszanie. Te [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]nie rozpoczynają podprocesu, chociaż pierwszy punkt wyraźnie mówi: „To, co jest w [nawiasach], nie jest wyrażeniem jak w wielu innych językach” - ale na pewno już jest! Jest to prawdopodobnie oczywiste dla doświadczonego czarodzieja bashów, ale nawet dla mnie natychmiast. Zamieszanie może powstać, ponieważ w nawiasach można używać pojedynczych nawiasów if, a nie wyrażeń w nawiasach podwójnych.
Peter - Przywróć Monikę

2
@protagonist ==nie jest tak naprawdę „bardziej idiomatyczny” niż =. Mają to samo znaczenie, ale ==jest wariantem ksh dostępnym również w bash i zsh, natomiast =jest przenośny. Nie ma naprawdę żadnej korzyści z używania ==i nie działa w zwykłym sh.
Gilles „SO- przestań być zły”

2
@WillSheppard Istnieje już wiele innych różnic między porównaniem a przypisaniem: porównanie jest w nawiasach, przypisanie nie; porównanie ma spacje wokół operatora, przypisanie nie; przypisanie ma nazwę zmiennej po lewej stronie, porównanie rzadko ma coś, co wygląda jak nazwa zmiennej po lewej stronie i możesz i powinieneś umieszczać cudzysłowy. Możesz pisać w ==środku [[ … ]], jeśli chcesz, ale =ma tę zaletę, że również pracuje [ … ], więc zalecam, aby w ogóle nie przyzwyczajać się do używania ==.
Gilles „SO- przestań być zły”

34

bardzo blisko

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

powinno działać.

rozkładam to

[[ $varA -eq 1 ]] 

to porównanie liczb całkowitych, gdy as

$varB == 't1'

to porównanie ciągów znaków. w przeciwnym razie po prostu poprawnie grupuję porównania.

Podwójne nawiasy kwadratowe ograniczają wyrażenie warunkowe. I uważam, że poniższe zdanie jest dobrym lektorem na ten temat: „(IBM) Test diagnostyczny, [, [[, ((i jeśli-to-jeszcze”)


Dla pewności: cytowanie nie 't1'jest konieczne, prawda? Ponieważ w przeciwieństwie do instrukcji arytmetycznych w podwójnych nawiasach, gdzie t1byłaby zmienna, t1w wyrażeniu warunkowym w podwójnych nawiasach jest tylko literał. Tj. Czy [[ $varB == 't1' ]]to dokładnie to samo [[ $varB == t1 ]], prawda?
Peter - Przywróć Monikę

6

Bardzo przenośna wersja (nawet do starszej powłoki Bourne'a):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Ma to dodatkową jakość działania tylko jednego podprocesu (co jest procesem [), niezależnie od smaku powłoki.

Wymienić =z -eqJeśli zmienne zawierają wartości liczbowe, np

  • 3 -eq 03 to prawda, ale
  • 3 = 03to fałsz. (porównanie ciągów)

3

Oto kod krótkiej wersji instrukcji if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Zwróć uwagę na następujące kwestie:

  1. ||i &&operandy wewnątrz, jeśli warunek (tj. między okrągłymi nawiasami) są operandami logicznymi (lub / i)

  2. ||i &&operandy na zewnątrz, jeśli warunki oznaczają wtedy / else

Praktycznie stwierdzenie mówi:

jeśli (a = 1 lub b = 2) to „ok” else „nok”


Nawias ( ... )tworzy podpowłokę. Może chcieć użyć nawiasów klamrowych{ ... } zamiast tego . Każdy stan utworzony w podpowłoce nie będzie widoczny w dzwoniącym.
Clint Pachl

0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.