Apakah ada operator “in” di bash / bourne?

17

Saya mencari operator "dalam" yang bekerja seperti ini:

if [ "$1" in ("cat","dog","mouse") ]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Ini jelas pernyataan yang jauh lebih pendek dibandingkan dengan, katakanlah, menggunakan beberapa tes "atau".

mrjayviper
sumber

Jawaban:

31

Anda bisa menggunakan case...esac

$ cat in.sh 
#!/bin/bash

case "$1" in 
  "cat"|"dog"|"mouse")
    echo "dollar 1 is either a cat or a dog or a mouse"
  ;;
  *)
    echo "none of the above"
  ;;
esac

Ex.

$ ./in.sh dog
dollar 1 is either a cat or a dog or a mouse
$ ./in.sh hamster
none of the above

Dengan ksh, bash -O extglobatau zsh -o kshglob, Anda juga bisa menggunakan pola gumpalan diperpanjang:

if [[ "$1" = @(cat|dog|mouse) ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi

Dengan bash, ksh93atau zsh, Anda juga bisa menggunakan perbandingan ekspresi reguler:

if [[ "$1" =~ ^(cat|dog|mouse)$ ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi
Steeldriver
sumber
ada alasan untuk kurung ganda? Terima kasih lagi!
mrjayviper
1
@mrjayviper tanda kurung ganda adalah tes konstruksi yang diperpanjang - AFAIK operator regex =~tidak valid di dalam tes braket tunggal POSIX
steeldriver
1
@steeldriver Only [adalah POSIX, tetapi [[merupakan fitur bash, ksh (tampaknya ini berasal dari sana, dan casecontoh zsh. Contohnya adalah yang paling POSIX dari semua,
Sergiy Kolodyazhnyy
2
@ StéphaneChazelas Sejak setidaknya bash 4.1-alpha tidak perlu mengatur extglog secara eksplisit. Dari perubahan bash : s. Paksa extglob aktif untuk sementara saat mem-parsing argumen pola ke operator == dan! = Ke perintah [[, untuk kompatibilitas.
Isaac
@steeldriver $ 1 dalam case "$1" intidak perlu dikutip, tidak ada pemisahan kata atau perluasan pathname dilakukan dalam token itu.
Isaac
9

Tidak ada tes "in" di bash, tetapi ada tes regex (tidak di bourne):

if [[ $1 =~ ^(cat|dog|mouse)$ ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Dan biasanya ditulis menggunakan variabel (lebih sedikit masalah dengan penawaran):

regex='^(cat|dog|mouse)$'

if [[ $1 =~ $regex ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Untuk shell Bourne yang lebih lama Anda harus menggunakan korek api:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac
Ishak
sumber
Ini jawaban yang diterima saya.
Nam G VU
6

Menggunakan sebuah case baik-baik saja dan baik jika Anda memiliki satu set hewan peliharaan yang ingin Anda lawan. Tapi itu tidak akan berfungsi jika Anda perlu membangun pola dalam runtime, karena casetidak menafsirkan pergantian dari dalam parameter yang diperluas.

Ini hanya akan cocok dengan string literal cat|dog|mouse :

patt='cat|dog|mouse'
case $1 in 
        $patt) echo "$1 matches the case" ;; 
esac

Namun, Anda dapat menggunakan variabel dengan kecocokan ekspresi reguler. Selama variabel tidak dikutip, setiap operator regex di dalamnya memiliki makna khusus.

patt='cat|dog|mouse'
if [[ "$1" =~ ^($patt)$ ]]; then
        echo "$1 matches the pattern"
fi

Anda juga bisa menggunakan array asosiatif. Memeriksa apakah ada kunci dalam satu adalah hal terdekat dengan inoperator yang diberikan Bash. Meskipun sintaksnya agak jelek:

declare -A arr
arr[cat]=1
arr[dog]=1
arr[mouse]=1

if [ "${arr[$1]+x}" ]; then
        echo "$1 is in the array"
fi

( ${arr[$1]+x}perluas xjika arr[$1]disetel, kosongkan sebaliknya. )

ilkkachu
sumber
5

Anda bisa menggunakan casepernyataan dalam iftes, tetapi kode itu akan terlihat sedikit berbulu:

if case "$1" in (cat|dog|mouse) true ;; (*) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

atau sedikit lebih pendek,

if ! case "$1" in (cat|dog|mouse) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

Ini menggunakan caseklausa hanya untuk melakukan pencocokan pola untuk ifklausa. Ini memperkenalkan tes benar / salah yang tidak perlu.

Lebih baik menggunakan case:

case "$1" in
    cat|dog|mouse)
        printf '"%s" is one of cat, dog or mouse\n' "$1"
        ;;
    *)
        printf '"%s" is unknown\n' "$1"
esac

Jangan lakukan ini:

is_one_of () {
    eval "case $1 in ($2) return 0; esac"
    return 1
}

if is_one_of "$1" 'cat|dog|mouse'; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

atau ini:

is_one_of () (
    word=$1
    shift
    IFS='|'
    eval "case $word in ($*) return 0; esac"
    return 1
)

if is_one_of "$1" cat dog mouse; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

... karena Anda hanya menambahkan cruft yang lebih berbahaya, hanya untuk dapat menggunakan ifpernyataan dalam kode Anda sebagai pengganti casepernyataan yang masuk akal .

Kusalananda
sumber
Bukankah itu lebih baik untuk membagi kasus menjadi panggilan fungsi dan mengevaluasi status keluar di dalam pernyataan if?
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Dan biarkan polanya menjadi argumen untuk fungsi? Itu harus melakukan salah evalsatu casepernyataan dalam kasus itu, dan itu akan lebih rentan terhadap kesalahan.
Kusalananda
Dalam hal ini ada pola sederhana, masing-masing dapat dilewatkan sebagai parameter posisi untuk berfungsi dan dilakukan "$1"|"$2"|"$3". Juga unix.stackexchange.com/a/234415/85039 Tapi ya, ini sedikit berbulu.
Sergiy Kolodyazhnyy
4

grep pendekatan.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -q untuk menghindari output ke layar (lebih cepat mengetik dari >/dev/null ).
  • -E untuk ekspresi reguler yang diperluas (cat|dog|mouse) perlu aspek ini.
  • ^(cat|dog|mouse)$cocok dengan semua garis yang dimulai ( ^) dengan kucing, anjing atau tikus ( (cat|dog|mouse)) diikuti oleh ujung garis ( $)
steve
sumber