Bir değişkeni durum koşulu olarak nasıl kullanabilirim?


17

|Bir caseifade testi olarak ayrılmış farklı dizeleri oluşan bir değişken kullanmaya çalışıyorum . Örneğin:

string="\"foo\"|\"bar\""
read choice
case $choice in
    $string)
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

İfadenin ilk kısmını yazabilmek fooveya barçalıştırabilmek istiyorum case. Ancak, ikisini de al foove barikinciye götür:

$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!

"$string"Bunun yerine kullanılması $stringfark etmez. Kullanmak da değil string="foo|bar".

Bu şekilde yapabileceğimi biliyorum:

case $choice in
    "foo"|"bar")
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

Çeşitli geçici çözümler düşünebilirim ama bir değişkeni casebash içinde bir koşul olarak kullanmanın mümkün olup olmadığını bilmek istiyorum . Bu mümkün mü ve eğer öyleyse, nasıl?


2
Gerçek bir cevap olarak önermek dilim varmıyor ama kimsenin söz olduğu için sadece olabilir bir durum deyimi sarmak eval$ seçim parens, yıldız, noktalı virgülü ve satırbaşları kaçan. Çirkin, ama "çalışıyor".
Jeff Schaller

@JeffSchaller - çoğu zaman kötü bir fikir değil ve belki de bu durumda sadece bilet. ben de tavsiye düşündü, ama readbiraz beni durdurdu. Bu gibi görünen olduğunu Bence kullanıcı girişi doğrulama, içinde, casedesenler gerektiğini değil değerlendirme listesinin en üstünde olacak ve oldukça aşağı budanmış olmalıdır *varsayılan desen orada ulaşmak sadece sonuçlar kabul edilebilir garantilidir şekilde. yine de, sorun ayrıştırma / genişletme sırası olduğundan, ikinci bir değerlendirme gerekli olabilir.
mikeserv

Yanıtlar:


14

Bash el kitabı şunları belirtir:

[[(] desen [| desen] ...) listesindeki vaka kelimesi ;; ] ... esac

İncelenen her bir desen tilde genişletme, parametre ve değişken genişletme, aritmetik ikame, komut ikamesi ve işlem ikamesi kullanılarak genişletilir.

«Yol adı genişletmesi» yok

Bu nedenle, bir desen «Yol adı genişletmesi» ile genişletilmez.

Bu nedenle: bir desen "|" içeremez içeride. Sadece: "|" ile iki örüntü birleştirilebilir.

Bu çalışıyor:

s1="foo"; s2="bar"    # or even s1="*foo*"; s2="*bar*"

read choice
case $choice in
    $s1|$s2 )     echo "Two val choice $choice"; ;;  # not "$s1"|"$s2"
    * )           echo "A Bad  choice! $choice"; ;;
esac

«Genişletilmiş Globbing» Kullanımı

Ancak, «Yol Adı Genişletme» kuralları kullanılarak wordeşleştirilir pattern.
Ve «globbing Genişletilmiş» burada , burada ve burada desenleri | dönüşümlü ( "") kullanımına izin verir.

Bu da işe yarar:

shopt -s extglob

string='@(foo|bar)'

read choice
    case $choice in
        $string )      printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )      printf 'Two val choice %-20s' "$choice"; ;;
        *)             printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
echo

Dize içeriği

Sonraki test komut dosyası, herhangi bir yerde fooveya barherhangi bir yerde bulunan tüm satırlarla eşleşen desenin '*$(foo|bar)*'veya iki değişkenin olduğunu $s1=*foo*ve$s2=*bar*


Test komut dosyası:

shopt -s extglob    # comment out this line to test unset extglob.
shopt -p extglob

s1="*foo*"; s2="*bar*"

string="*foo*"
string="*foo*|*bar*"
string='@(*foo*|*bar)'
string='*@(foo|bar)*'
printf "%s\n" "$string"

while IFS= read -r choice; do
    case $choice in
        "$s1"|"$s2" )   printf 'A first choice %-20s' "$choice"; ;;&
        $string )   printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )   printf 'Two val choice %-20s' "$choice"; ;;
        *)      printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
    echo
done <<-\_several_strings_
f
b
foo
bar
*foo*
*foo*|*bar*
\"foo\"
"foo"
afooline
onebarvalue
now foo with spaces
_several_strings_

9

extglobSeçeneği kullanabilirsiniz :

shopt -s extglob
string='@(foo|bar)'

İlginç; ile @(foo|bar)karşılaştırıldığında özel kılan foo|barnedir? Her ikisi de, tam olarak yazıldığında aynı şekilde çalışan geçerli kalıplardır.
chepner

4
Ah boşver. |bir kalıbın parçası değil , bir cümlede birden çok kalıba izin vermek foo|bariçin caseifadenin sözdiziminin bir parçasıdır . | olduğu olsa uzatılmış desen parçası.
chepner

3

Desenler genişletilmeden öncecase veya |borusu ayrıştırıldığı için iki değişkene ihtiyacınız vardır.

v1=foo v2=bar

case foo in ("$v1"|"$v2") echo foo; esac

foo

Değişkenlerdeki kabuk kalıpları da tırnak içine alındığında veya alıntı yapılmadığında farklı şekilde ele alınır:

q=?

case a in
("$q") echo question mark;;
($q)   echo not a question mark
esac

not a question mark

-1

Kısa çizgi uyumlu bir çözüm istiyorsanız, şunu yazabilirsiniz:

  string="foo|bar"
  read choice
  awk 'BEGIN{
   n=split("'$string'",p,"|");
   for(i=1;i<=n;i++)
    if(system("\
      case \"'$choice'\" in "p[i]")\
       echo \"You chose '$choice'\";\
       exit 1;;\
      esac"))
     exit;
   print "Bad choice"
  }'

Açıklama: awk "string" i bölmek ve her parçayı ayrı ayrı test etmek için kullanılır. Eğer "seçim" o anda test edilen p [i] parçasına uyuyorsa, awk-komutu 11. satırdaki "çıkış" ile sonlandırılacaktır. Çok test için, kabuğun "durumu" kullanılır (bir 'sistem' çağrısı içinde) , terdon tarafından sorduğu gibi. Bu, kabuğun "kasası" nın izin verdiği gibi, "foooo" ("arama deseni") ile eşleşmek için, örneğin "foo * | bar" için amaçlanan test "dizgisini" değiştirme olanağını korur. Bunun yerine normal ifadeleri tercih ederseniz, "system" -call parametresini atlayabilir ve awk "~" veya "match" kullanabilirsiniz.


Sevgili downvoter, bana downvote nedenini söylerseniz işe yaramaz çözüm adaylarından nasıl kaçınacağımı daha iyi öğrenebilirim.
Gerald Schade
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.