trường hợp + cách thực hiện bằng hoặc ít hơn hoặc lớn hơn trong cú pháp trường hợp


9

Mục tiêu của tôi là xác minh một phạm vi số với (chỉ với case+ esac) và in phạm vi đó. Ví dụ:

  • Nếu số nằm trong khoảng từ 0 đến 80, hãy in >=0<=80
  • Nếu số nằm trong khoảng từ 81 đến 100 thì in >=81<=100
  • Vân vân.

Vấn đề với tập lệnh của tôi bên dưới chỉ in >=0<=90nếu số từ 0 đến 9. Làm cách nào để sửa tập lệnh của tôi, để nó sẽ in đầu ra chính xác theo phạm vi số?

#!/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

Câu trả lời:


6

casechỉ cho phù hợp với mô hình, nó sẽ không làm đánh giá số học (ngoại trừ có lẽ nếu bạn xem xét zsh's <x-y>điều hành phù hợp với mô hình mở rộng). Các [...]chỉ để phù hợp với một nhân vật (hoặc yếu tố đối chiếu trong một số triển khai) dựa trên bộ định bên trong. Vì vậy, ví dụ như [0-80]sẽ phù hợp với một nhân vật nếu nó là một trong những 0để 8hoặc 0(có nghĩa là, một từ 0, 1, 2, 3, 4, 5, 6, 7, 8).

Bạn có thể ghép các số với các mẫu như:

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

Nhưng bạn có thể dễ dàng thấy rằng nó không phải là công cụ phù hợp.

Các ký tự [...]khớp với một ký tự so với danh sách các ký tự được chỉ định, do đó [121-300]khớp với bất kỳ ký tự nào là 1, 2, 1 đến 3, 0 hoặc 0, do đó, nó giống như [0-3]hoặc [0123].

Sử dụng:

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

Một cách khác để sử dụng casesẽ như sau:

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

Hoặc sử dụng toán tử 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...

Hoặc như @mikeerv, suy nghĩ bên ngoài hộp, đảo ngược caselogic và khớp 1với giá trị của những so sánh số học đó .


1
+1, xem xét if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Ít gõ, ít bị lỗi.
peterph

@peterph Nó cũng mất nhiều thời gian hơn để chạy.
Ken Sharp

4

Trên thực tế điều này thực sự dễ dàng để làm. Điều quan trọng caselà nó sẽ luôn mở rộng hết mức cần thiết để tìm trận đấu đầu tiên so với một mẫu. Đó là hành vi cụ thể. Và vì vậy, bạn có thể chỉ cần thiết lập nó với một chuỗi đã biết và đánh giá các mở rộng của các mẫu.

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

casesẽ không bao giờ mở rộng thêm bất kỳ mẫu nào trong số các mẫu đó để tìm được số 1 trong mẫu. Điều này đặc biệt quan trọng khi làm việc với đầu vào của người dùng, bởi vì điều đó có nghĩa là bạn có thể xác minh một cách an toàn các nội dung $numbertrước khi cố gắng đặt nó trong bối cảnh mở rộng số học trong cùng một tuyên bố trường hợp mà bạn thực sự đưa nó vào một mở rộng toán học.


Tôi thích cách bạn nghĩ bên ngoài / xung quanh hộp.
Stéphane Chazelas

@ StéphaneChazelas - tôi thích case. Có một số thứ hay ho bạn có thể làm với $((toán học ))case- đặc biệt là các bài tập xung quanh trong các mẫu không bao giờ xảy ra cho đến khi chúng cần - và thậm chí bạn có thể xây dựng các cây phân tích mở rộng các hồi quy lồng nhau nếu bạn tập hợp các mẫu bằng một aliaschuỗi. Đó là cách nhanh nhất mà tôi đã tìm thấy để có được một trình bao để thực hiện các công việc như dịch ký tự và hoán đổi các ký tự cho các giá trị byte. nó có thể khá nhanh - Trường hợp xấu nhất của C-Locale ASCII + <> bát phân là 7 lần mở rộng mẫu POSIX cơ bản.
mikeerv

1

Điều này không tốt lắm nhưng bạn có thể sử dụng:

 #!/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

Bạn có thể muốn "chuẩn hóa" số bằng $ ((số $)) để bao gồm các số như "001" hoặc "0x99" ... Điều đó cũng có thể bao gồm "12" và "12 + 12" có thể hoặc có thể không được mong muốn
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.