Bir kabuk if ifadesinde birden çok koşulu nasıl temsil edebilirim?


308

Bunun gibi birden çok koşulu temsil etmek istiyorum:

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

ama senaryoyu çalıştırdığımda

syntax error at line 15: `[' unexpected, 

burada satır 15 eğer gösteren olduğunu ....

Bu durumun sorunu ne? Sanırım ile ilgili bir sorun var ().


6
Kabuk koşullarını değil, test koşullarını soruyorsunuz . Örneğinizdeki tüm ifade kabuk tarafından değil test( [) ile değerlendirilir . Kabuk sadece çıkış durumunu değerlendirir [.
ceving


Yanıtlar:


381

Klasik teknik (kaçış metakarakterleri):

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

Ben referansları $gçift ​​tırnak içine aldım ; genel olarak bu iyi bir uygulamadır. Kesinlikle, parantezlere gerek yoktur çünkü önceliği -ave -oonlarsız da doğru yapar.

Not, -ave -ooperatör için POSIX tarifnamenin bir parçası olan testdiğer adıyla, [esas olarak (bunlar bir parçası olması nedeniyle için geriye dönük uyumluluk, testörneğin, 7. Baskı UNIX), ancak açıkça POSIX tarafından 'eskimiş' olarak işaretlenir. Bash ( koşullu ifadelere bakın ) , kendi alternatif operatörleri için -ave -oargümanlar alan klasik ve POSIX anlamlarını ön planda tutuyor gibi görünüyor .


Biraz özenle, daha modern bir [[operatör kullanabilirsiniz , ancak Bash ve Korn Shell'deki sürümlerin (örneğin) aynı olması gerekmediğini unutmayın.

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

Mac OS X'te Bash 3.2.57 kullanarak örnek çalıştırma:

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

Değişkenleri [[yaptığınız gibi alıntılamanıza gerek yoktur, [çünkü bu aynı şekilde ayrı bir komut değildir [.


Klasik bir soru değil mi?

Ben de öyle düşünürdüm. Bununla birlikte, başka bir alternatif var, yani:

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

Gerçekten de, autoconfaraç veya ilgili paketler için 'taşınabilir kabuk' yönergelerini okursanız , bu gösterim - ' ||' ve ' &&' kullanarak - önerdikleri şeydir. Sanırım şu ana kadar gidebilirsin:

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

Eylemlerin yankılanma kadar önemsiz olduğu yerlerde, bu kötü değildir. Tekrarlanacak eylem bloğu birden çok satır olduğunda, tekrar çok acı vericidir ve önceki sürümlerden biri tercih edilir - veya eylemleri farklı thenbloklarda çağrılan bir işleve sarmanız gerekir .


9
Kaçan parantezlerin işe yaradığını bilmek güzel; Bir kenara olarak: özellikle bu durumda, parantezler bile gerekli değildir -a, aslında daha yüksek önceliğe sahiptir -o(farklı &&ve ||kabuk - ancak, içinde bash [[ ... ]] Koşullamalar , && aynı zamanda daha yüksek önceliğe sahip ||). POSIX man sayfasının kendisinin önerdiği maksimum sağlamlık ve taşınabilirlikten kaçınmak -ave isterseniz, gruplama için alt-oif ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
kabukları

İyi bir çözüm, ancak tüm parantez ve parantez olmadan formu seviyorum:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK 17:08

Buna biraz geç kaldım, ancak yine de en iyi arama sonucudur, bu yüzden &&veya ||diğer dillerdeki koşullara daha çok benzediği için veya tercih edildiğini ve kısa devre koşullarını size izin verdiğini belirtmek isterim . Örneğin: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Bu şekilde, check_inodesboşsa, iki çağrıyı önlersiniz stat, ancak daha büyük, karmaşık bir test koşulu yürütmeden önce tüm argümanları işlemelidir (bu davranışı unutursanız da hatalara yol açabilir).
Haravikk

182

Bash'ta:

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

9
Kesinlikle en iyi yaklaşım bash. Bir kenara gibi: parantezler bile, çünkü gerekli olmayan bu özel durumda içeride [[ ... ]] Koşullamalar && aslında yüksek önceliğe daha vardır ||- aksine dış böyle Koşullamalar.
mklement0

Burada hangi koşulun eşleştiğini öğrenmenin bir yolu var mı? İlk parantez içinde mi yoksa diğerinde mi?
Firelord

1
@Firelord: Bir içine koşulları ayırmanız gerekir if/ elsedeyimi ile arasındaki koduna sahip thenve fibunu tekrarlamaktan kaçınmak amacıyla bir fonksiyonu koydu. Çok basit durumlarda, caseaçıklamalı bir ifade kullanabilirsiniz ;&(Bash 4'te).
sonraki duyuruya kadar duraklatıldı.

1
İki köşeli parantezin amacı nedir?
peterchaula


37

/bin/bashAşağıdakileri kullanmak işe yarayacaktır:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

Dize değişkenlerinizde boşluk varsa ve varlığını kontrol ediyorsanız dikkatli olun. Onları doğru şekilde alıntıladığınızdan emin olun.

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

1
dolar sembolünden sonra kıvırcık parantez kullandığımızda !!
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

2
Bu işe yaramaz bir alt kabuk. { ;}yerine kullanılabilir.
phk

2

Dize karşılaştırması için bash'de aşağıdaki tekniği kullanabilirsiniz.

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

Misal:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
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
Stack Overflow'a hoş geldiniz! Bu soru sadece çalışma kodu için değil, bir açıklama arıyor . Cevabınız soru soran kişiye bir fikir vermiyor ve silinebilir. Gözlenen semptomlara neyin neden olduğunu açıklamak için lütfen düzenleyin .
Toby Speight

0

2'den fazla koşulu da zincirleyebilirsiniz:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.