case + cara menerapkan sintaksis sama atau kurang atau lebih besar

9

Target saya adalah memverifikasi rentang angka dengan (hanya dengan case+ esac), dan mencetak rentangnya. Jadi misalnya:

  • Jika angkanya antara 0 dan 80, cetak >=0<=80
  • Jika angkanya antara 81 dan 100 maka cetak >=81<=100
  • dll.

Masalah dengan skrip saya di bawah hanya mencetak >=0<=90jika angka antara 0 dan 9. Bagaimana cara memperbaiki skrip saya, sehingga akan mencetak hasil yang benar sesuai dengan kisaran nomor?

#!/bin/ksh
read number 
case $number in 
 [0-80])  echo ">=0<=80";; 
 [81-100]) echo ">=81<=100";; 
 [101-120]) echo ">=101<=120";;
 [121-300]) echo ">=121<=300";;
esac
yael
sumber

Jawaban:

6

casehanya untuk pencocokan pola, tidak akan melakukan evaluasi aritmatika (kecuali mungkin jika Anda mempertimbangkan zsh's <x-y>Operator pencocokan pola diperpanjang). The [...]hanya untuk mencocokkan satu karakter (atau menyusun unsur dalam beberapa implementasi) berdasarkan pada set yang ditentukan dalam. Jadi misalnya [0-80]akan cocok satu karakter jika salah satu dari 0ke 8atau 0(yaitu, salah satu dari 0, 1, 2, 3, 4, 5, 6, 7, 8).

Anda dapat mencocokkan angka dengan pola seperti:

case $(($number)) in
  ([0-9]|[1-7][0-9]|80) echo ">=0<=80";;
  (8[1-9]|9[0-9]|100) echo ">=81<=100";;
  ... and so on
esac

Tetapi Anda dapat dengan mudah melihat bahwa itu bukan alat yang tepat.

The [...]cocok satu karakter terhadap daftar karakter yang ditentukan, sehingga [121-300]pertandingan untuk setiap karakter yang baik 1, 2, 1 sampai 3, 0 atau 0, jadi sama dengan [0-3]atau [0123].

Menggunakan:

if [ "$number" -ge 0 ] && [ "$number" -le 80 ]; then
  echo ">=0<=80"
elif [ "$number" -ge 81 ] &&  [ "$number" -le 100 ]; then
  echo ">=81<=100"
elif ... and so on
  ...
fi

Cara lain untuk digunakan caseadalah:

case $((
  (number >= 0 && number <= 80)   * 1 +
  (number > 80 && number <= 100)  * 2 +
  (number > 100 && number <= 120) * 3 +
  (number > 120 && number <= 300) * 4)) in
  (1) echo ">=0<=80";;
  (2) echo ">=81<=100";;
  (3) echo ">=101<=120";;
  (4) echo ">=121<=300";;
  (0) echo "None of the above";;
esac

Atau gunakan operator ternary ( x ? y : z):

case $((
  number >= 0 && number <= 80   ? 1 :
  number > 80 && number <= 100  ? 2 :
  number > 100 && number <= 120 ? 3 :
  number > 120 && number <= 300 ? 4 : 0)) in...

Atau seperti @mikeserv, pikirkan di luar kotak, balikkan caselogika dan cocokkan 1dengan nilai perbandingan aritmatika tersebut .

Stéphane Chazelas
sumber
1
+1, pertimbangkan if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Lebih sedikit mengetik, lebih sedikit kesalahan.
peterph
@pheterph Ini juga membutuhkan waktu lebih lama untuk dijalankan.
Ken Sharp
4

Sebenarnya ini sangat mudah dilakukan. Masalahnya caseadalah bahwa ia akan selalu berkembang hanya sebanyak yang diperlukan untuk menemukan kecocokan pertama melawan suatu pola. Itu perilaku khusus. Jadi Anda bisa mengaturnya dengan string yang dikenal dan mengevaluasi ekspansi pola.

case  1:${number:--} in
(1:*[!0-9]*|1:0*[89]*)
  ! echo NAN
;;
($((number<81))*)
    echo "$number >=0<=80"
;;
($((number<101))*)
    echo "$number >=81<=100"
;;
($((number<121))*)
    echo "$number >=101<=120"
;;
($((number<301))*)
    echo "$number >=121<=300"
;;
esac

casetidak akan pernah memperluas lebih dari pola-pola itu daripada harus untuk menemukan 1 terkemuka dalam pola. Ini sangat penting ketika bekerja dengan input pengguna, karena itu berarti Anda dapat dengan aman memverifikasi konten $numbersebelum mencoba memasukkannya ke dalam konteks ekspansi aritmatika dalam pernyataan kasus yang sama di mana Anda benar-benar memasukkannya ke dalam ekspansi matematika.

mikeserv
sumber
👍 Saya suka cara Anda berpikir di luar / di sekitar kotak.
Stéphane Chazelas
@ StéphaneChazelas - saya suka case. ada beberapa hal keren yang dapat Anda lakukan dengan $((matematika ))dan case- terutama tugas-tugas di sekitarnya dalam pola yang tidak pernah terjadi sampai mereka perlu - dan Anda bahkan dapat membangun pohon parse yang memperluas rekursi bersarang jika Anda mengisi pola dengan aliasrantai. ini adalah cara tercepat yang pernah saya temukan untuk mendapatkan shell untuk melakukan hal-hal seperti terjemahan karakter dan untuk menukar karakter dengan nilai byte. itu bisa sangat cepat - C-Lokal ASCII + <> oktal kasus terburuk adalah 7 perluasan pola POSIX dasar.
mikeserv
1

Ini tidak terlalu bagus tetapi Anda dapat menggunakan ini:

 #!/bin/ksh

read number  

case $number in
[0-9]|[1-7][0-9]|80) echo  echo ">=0<=80";;
8[1-9]|9[0-9]|100) echo ">=81<=100";;
10[1-9]|11[0-9]|120) echo ">=101<=120";;
12[1-9]|130) echo ">=121<=300";;
esac
Poulpatine
sumber
Anda mungkin ingin "membuat kanon" nomor dengan $ (($ number)) untuk menutupi nomor seperti "001" atau "0x99" ... Itu juga akan mencakup untuk "12" dan "12 + 12" yang mungkin atau mungkin tidak diinginkan.
Stéphane Chazelas