case + wie man in der case-Syntax gleich oder kleiner oder größer implementiert

9

Mein Ziel ist es, einen Zahlenbereich mit (nur mit case+ esac) zu überprüfen und den Bereich auszudrucken. Also zum Beispiel:

  • Wenn die Zahl zwischen 0 und 80 liegt, drucken Sie >=0<=80
  • Wenn die Zahl zwischen 81 und 100 liegt, drucken Sie >=81<=100
  • usw.

Das Problem mit meinem Skript unten wird nur gedruckt, >=0<=90wenn die Zahl zwischen 0 und 9 liegt. Wie kann ich mein Skript reparieren, damit es die korrekte Ausgabe entsprechend dem Nummernkreis druckt?

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

Antworten:

6

caseist nur für den Mustervergleich vorgesehen, führt jedoch keine arithmetische Auswertung durch (außer wenn Sie zshden <x-y>erweiterten Mustervergleichsoperator in Betracht ziehen ). Das [...]soll nur mit einem Zeichen (oder einem Sortierelement in einigen Implementierungen) übereinstimmen, basierend auf dem darin angegebenen Satz. So zum Beispiel [0-80]würde Übereinstimmen einen Charakters , wenn es eine von 0bis 8oder 0(das heißt, eines von 0, 1, 2, 3, 4, 5, 6, 7, 8).

Sie können Zahlen mit Mustern wie:

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

Aber Sie können leicht erkennen, dass es nicht das richtige Werkzeug ist.

Das [...]entspricht einem Zeichen mit der Liste der angegebenen Zeichen, entspricht also [121-300]jedem Zeichen, das entweder 1, 2, 1 bis 3, 0 oder 0 ist. Es ist also dasselbe wie [0-3]oder [0123].

Benutzen:

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

Eine andere Art zu verwenden casewäre wie:

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

Oder verwenden Sie den ternären Operator ( 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...

Oder denken Sie wie bei @mikeserv über den Tellerrand hinaus, kehren Sie die caseLogik um und vergleichen Sie sie mit 1dem Wert dieser arithmetischen Vergleiche .

Stéphane Chazelas
quelle
1
+1, überlege if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Weniger tippen, weniger fehleranfällig.
Peterph
@peterph Die Ausführung dauert auch länger.
Ken Sharp
4

Eigentlich ist das ganz einfach. Die Sache dabei caseist, dass es sich immer nur so weit ausdehnt, wie es nötig ist, um die erste Übereinstimmung mit einem Muster zu finden. Das ist spezifiziertes Verhalten. Sie können es also einfach mit einer bekannten Zeichenfolge einrichten und die Erweiterungen der Muster auswerten.

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

casewird niemals mehr dieser Muster erweitern als nötig, um eine führende 1 im Muster zu finden. Dies ist besonders wichtig, wenn Sie mit Benutzereingaben arbeiten, da Sie den Inhalt sicher überprüfen können, $numberbevor Sie versuchen, ihn in einen arithmetischen Erweiterungskontext in derselben case-Anweisung zu stellen, in der Sie ihn tatsächlich in eine mathematische Erweiterung einfügen.

mikeserv
quelle
👍 Ich mag die Art und Weise, wie Sie außerhalb / um die Box denken.
Stéphane Chazelas
@ StéphaneChazelas - ich mag case. Es gibt einige coole Dinge, die Sie mit $((Mathematik tun können - ))und caseinsbesondere Aufgaben in Mustern, die erst dann auftreten, wenn sie benötigt werden - und Sie können sogar Analysebäume erstellen, die verschachtelte Rekursionen erweitern, wenn Sie die Muster mit einer aliasKette füllen. Es ist der schnellste Weg, eine Shell dazu zu bringen, Dinge wie die Zeichenübersetzung zu erledigen und Zeichen gegen Bytewerte auszutauschen. es kann ziemlich schnell sein - C-Locale ASCII + <> oktal sehr schlimmster Fall sind 7 grundlegende POSIX-Mustererweiterungen.
Mikesserv
1

Das ist nicht sehr schön, aber Sie können dies verwenden:

 #!/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
quelle
Möglicherweise möchten Sie die Nummer mit $ (($ number)) "kanonisieren", um Nummern wie "001" oder "0x99" abzudecken ... Dies würde auch "12" und "12 + 12" abdecken, die möglicherweise oder möglicherweise nicht wünschenswert sein.
Stéphane Chazelas