case + jak zaimplementować składnię case równą lub mniejszą lub większą

9

Moim celem jest zweryfikowanie zakresu liczby za pomocą (tylko case+ esac) i wydrukowanie zakresu. Na przykład:

  • Jeśli liczba wynosi od 0 do 80, wydrukuj >=0<=80
  • Jeśli liczba wynosi od 81 do 100, wydrukuj >=81<=100
  • itp.

Problem z moim skryptem poniżej jest drukowany tylko >=0<=90wtedy, gdy liczba między 0 a 9. Jak naprawić mój skrypt, aby drukował prawidłowe dane wyjściowe zgodnie z zakresem liczb?

#!/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
źródło

Odpowiedzi:

6

casejest tylko do dopasowania wzorca, to nie zrobi ocenę arytmetyczną (może z wyjątkiem jeśli wziąć pod uwagę zsh„s <x-y>rozszerzony wzór pasujący operatora). Należy [...]dopasować tylko jeden znak (lub element zestawiający w niektórych implementacjach) na podstawie zestawu podanego w. Tak na przykład [0-80], że pasuje do jednej postaci, jeśli jest to jeden 0z 8albo 0(to znaczy, jeden z 0, 1, 2, 3, 4, 5, 6, 7, 8).

Możesz dopasować liczby do wzorów takich jak:

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

Ale łatwo można zauważyć, że nie jest to właściwe narzędzie.

[...]Dopasowuje jeden znak występuje na liście określonych znaków, więc [121-300]meczów dla każdego znaku, który jest albo 1, 2, 1 do 3, 0 lub 0, więc to jest taka sama jak [0-3]lub [0123].

Posługiwać się:

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

Innym sposobem użycia casebyłoby:

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

Lub użyj operatora trójskładnikowego ( 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...

Lub jak @mikeserv, zastanów się nieszablonowo, odwróć caselogikę i dopasuj 1do wartości tych porównań arytmetycznych .

Stéphane Chazelas
źródło
1
+1, rozważ if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Mniej pisania, mniej podatny na błędy.
peterph
@peterph Uruchomienie również trwa dłużej.
Ken Sharp
4

W rzeczywistości jest to naprawdę łatwe do zrobienia. Chodzi o caseto, że zawsze będzie się powiększać tylko tyle, ile jest potrzebne do znalezienia pierwszego dopasowania do wzorca. To jest określone zachowanie. Możesz więc ustawić go ze znanym ciągiem i ocenić rozszerzenia wzorców.

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

casenigdy nie rozwinie więcej tych wzorów niż jest to konieczne, aby znaleźć wiodącą 1 we wzorze. Jest to szczególnie ważne podczas pracy z danymi wprowadzanymi przez użytkownika, ponieważ oznacza to, że możesz bezpiecznie zweryfikować zawartość $numberprzed próbą umieszczenia jej w kontekście rozwinięcia arytmetycznego w tej samej instrukcji przypadku, w której faktycznie umieścisz ją w rozwinięciu matematycznym.

mikeserv
źródło
👍 Podoba mi się sposób myślenia na zewnątrz / wokół pudełka.
Stéphane Chazelas
@ StéphaneChazelas - lubię case. są pewne fajne rzeczy, które możesz zrobić z $((matematyką ))i case- zwłaszcza otaczającymi zadaniami w wzorach, które nigdy się nie zdarzają, dopóki nie będą musiały - i możesz nawet budować parsowania drzew, które rozszerzają zagnieżdżone rekurencje, jeśli zapełnisz wzory aliasłańcuchem. jest to najszybszy sposób, w jaki udało mi się uzyskać powłokę do wykonywania translacji znaków i zamiany znaków na wartości bajtów. może być dość szybki - C-Locale ASCII + <> ósemkowy najgorszy przypadek to 7 podstawowych rozszerzeń wzorca POSIX.
mikeserv
1

Nie jest to zbyt miłe, ale możesz użyć tego:

 #!/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
źródło
Możesz chcieć „kanonizować” liczbę za pomocą $ (($ number)) w celu pokrycia liczb takich jak „001” lub „0x99” ... To obejmowałoby również „12” i „12 + 12”, które mogą lub mogą nie być pożądanym.
Stéphane Chazelas,