Bagaimana cara merepresentasikan beberapa kondisi dalam pernyataan if shell?

308

Saya ingin mewakili beberapa kondisi seperti ini:

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

tetapi ketika saya menjalankan skrip, itu menunjukkan

syntax error at line 15: `[' unexpected, 

di mana baris 15 adalah yang menunjukkan jika ....

Apa yang salah dengan kondisi ini? Saya kira ada yang salah dengan ().

pengguna389955
sumber
6
Anda tidak bertanya tentang kondisi shell tetapi kondisi pengujian . Seluruh ekspresi dalam contoh Anda dievaluasi oleh test( [) dan bukan oleh shell. Shell hanya mengevaluasi status keluar dari [.
ceving

Jawaban:

381

Teknik klasik (escape metacharacters):

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

Saya telah menyertakan referensi $gdalam tanda kutip ganda; itu praktik yang baik, secara umum. Secara ketat, tanda kurung tidak diperlukan karena diutamakan -adan -omembuatnya benar bahkan tanpa mereka.

Perhatikan bahwa operator -adan -omerupakan bagian dari spesifikasi POSIX untuk testalias [, terutama untuk kompatibilitas mundur (karena mereka adalah bagian dari testUNIX Edisi ke-7, misalnya), tetapi mereka secara eksplisit ditandai sebagai 'usang' oleh POSIX. Bash (lihat ekspresi kondisional ) tampaknya mendahului makna klasik dan POSIX untuk -adan -odengan operator alternatifnya sendiri yang mengambil argumen.


Dengan hati-hati, Anda dapat menggunakan [[operator yang lebih modern , tetapi perlu diketahui bahwa versi di Bash dan Korn Shell (misalnya) tidak harus sama.

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

Contoh jalankan, menggunakan Bash 3.2.57 pada 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

Anda tidak perlu mengutip variabel [[seperti yang Anda lakukan [karena itu bukan perintah yang terpisah dengan cara yang [sama.


Bukankah itu pertanyaan klasik?

Saya akan berpikir begitu. Namun, ada alternatif lain, yaitu:

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

Memang, jika Anda membaca pedoman 'shell portabel' untuk autoconfalat atau paket terkait, notasi ini - menggunakan ' ||' dan ' &&' - adalah apa yang mereka rekomendasikan. Saya kira Anda bahkan bisa pergi sejauh:

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

Jika tindakannya sepele seperti gema, ini tidak buruk. Ketika blok aksi yang akan diulang adalah beberapa baris, pengulangan terlalu menyakitkan dan salah satu versi sebelumnya lebih disukai - atau Anda perlu membungkus aksi menjadi fungsi yang dipanggil dalam thenblok yang berbeda .

Jonathan Leffler
sumber
9
Adalah baik untuk mengetahui bahwa kurung yang lolos bekerja; sebagai tambahan: dalam kasus khusus ini, tanda kurung bahkan tidak diperlukan, karena -asebenarnya memiliki prioritas lebih tinggi daripada -o(tidak seperti &&dan ||di shell - namun, di dalam bash [[ ... ]] kondisional , && juga memiliki prioritas lebih tinggi daripada ||). Jika Anda ingin menghindari -adan -ountuk kekokohan dan portabilitas maksimum - yang halaman manual POSIX sendiri sarankan - Anda juga dapat menggunakan subkulit untuk pengelompokan:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0
Solusi yang bagus, tapi saya suka formulir tanpa tanda kurung dan kurung:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK
Saya agak terlambat untuk ini, tetapi ini masih merupakan hasil pencarian teratas, jadi saya hanya ingin mencatat bahwa menggunakan &&atau ||juga lebih disukai karena berperilaku lebih seperti conditional dalam bahasa lain, dan memungkinkan Anda kondisi arus pendek. Sebagai contoh: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Melakukannya dengan cara ini, jika check_inodeskosong, Anda menghindari dua panggilan stat, sedangkan kondisi pengujian yang lebih besar dan kompleks harus memproses semua argumen sebelum mengeksekusi (yang juga dapat menyebabkan bug jika Anda lupa tentang perilaku itu).
Haravikk
182

Di Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]
Dijeda sampai pemberitahuan lebih lanjut.
sumber
9
Jelas pendekatan terbaik di bash. Sebagai tambahan: dalam kasus khusus ini tanda kurung bahkan tidak diperlukan, karena kondisional di dalam sebenarnya memiliki prioritas lebih tinggi daripada - tidak seperti di luar kondisional seperti itu. [[ ... ]] &&||
mklement0
Adakah cara untuk mengetahui kondisi mana yang cocok di sini? Apakah itu yang ada dalam tanda kurung pertama atau yang lainnya?
Firelord
1
@Firelord: Anda harus memisahkan kondisi ke if/ elsepernyataan dan memiliki kode antara thendan fimemasukkan fungsi untuk menghindari mengulanginya. Dalam kasus yang sangat sederhana, Anda bisa menggunakan casepernyataan dengan ;&fallthrough (dalam Bash 4).
Dijeda sampai pemberitahuan lebih lanjut.
1
Apa tujuan dari dua tanda kurung?
peterchaula
2
@ Peter: Silakan lihat jawaban saya di sini , Konstruksi Bersyarat dalam Manual Bash dan BashFAQ / 31 .
Dijeda sampai pemberitahuan lebih lanjut.
37

Menggunakan /bin/bashyang berikut ini akan berhasil:

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

Hati-hati jika Anda memiliki spasi di variabel string Anda dan Anda memeriksa keberadaannya. Pastikan untuk mengutipnya dengan benar.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then
orkoden
sumber
1
ketika kita menggunakan kurung keriting setelah simbol dolar !!
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
sumber
2
Subshell yang tidak berguna. { ;}bisa digunakan sebagai gantinya.
phk
2

Dalam bash untuk perbandingan string, Anda dapat menggunakan teknik berikut.

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

Contoh:

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
sumber
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
sumber
3
Selamat Datang di Stack Overflow! Pertanyaan ini mencari penjelasan , bukan hanya untuk kode kerja. Jawaban Anda tidak memberikan wawasan untuk si penanya, dan dapat dihapus. Harap edit untuk menjelaskan apa yang menyebabkan gejala yang diamati.
Toby Speight
0

Anda juga dapat membuat lebih dari 2 kondisi:

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
sumber