case + comment implémenter une syntaxe de cas égal ou inférieur ou supérieur

9

Mon objectif est de vérifier une plage de nombres avec (uniquement avec case+ esac) et d'imprimer la plage. Ainsi, par exemple:

  • Si le nombre est compris entre 0 et 80, imprimez >=0<=80
  • Si le nombre est compris entre 81 et 100, imprimez >=81<=100
  • etc.

Le problème avec mon script ci-dessous ne s'imprime que >=0<=90si le nombre est compris entre 0 et 9. Comment corriger mon script, afin qu'il imprime la sortie correcte en fonction de la plage de numéros?

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

Réponses:

6

caseest uniquement pour la correspondance de modèles, il ne fera pas d'évaluation arithmétique (sauf peut-être si vous considérez zshl' <x-y>opérateur de correspondance de modèles étendu). Le [...]ne correspond qu'à un seul caractère (ou élément d'assemblage dans certaines implémentations) en fonction de l'ensemble spécifié dans. Ainsi , par exemple [0-80]correspondrait à un caractère si elle est l' un 0de 8ou 0(qui est l' un des 0, 1, 2, 3, 4, 5, 6, 7, 8).

Vous pouvez faire correspondre des nombres avec des modèles comme:

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

Mais vous pouvez facilement voir que ce n'est pas le bon outil.

Le [...]correspond à un caractère par rapport à la liste des caractères spécifiés, donc [121-300]correspond à tout caractère qui est soit 1, 2, 1 à 3, 0 ou 0, c'est donc la même chose que [0-3]ou [0123].

Utilisation:

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

Une autre façon d'utiliser caseserait comme:

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

Ou utilisez l'opérateur ternaire ( 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...

Ou comme @mikeserv, sortez des sentiers battus, inversez la caselogique et1 comparez avec la valeur de ces comparaisons arithmétiques .

Stéphane Chazelas
la source
1
+1, considérez if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Moins de frappe, moins sujet aux erreurs.
peterph
@peterph L'exécution prend également plus de temps.
Ken Sharp
4

En fait, c'est vraiment facile à faire. Le problème, casec'est qu'il ne s'agrandira toujours que si nécessaire pour trouver la première correspondance avec un motif. C'est un comportement spécifié. Et donc vous pouvez simplement le configurer avec une chaîne connue et évaluer les extensions des modèles.

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

casene développera jamais plus de ces modèles que nécessaire pour trouver un 1 de tête dans le modèle. Cela est particulièrement important lorsque vous travaillez avec des entrées utilisateur, car cela signifie que vous pouvez vérifier en toute sécurité le contenu de $numberavant d'essayer de le placer dans un contexte d'expansion arithmétique dans la même instruction de cas dans laquelle vous le placez réellement dans une expansion mathématique.

mikeserv
la source
👍 J'aime votre façon de penser à l'extérieur / autour de la boîte.
Stéphane Chazelas
@ StéphaneChazelas - j'aime case. Il y a des trucs sympas que vous pouvez faire avec les $((mathématiques ))et case- en particulier les affectations environnantes dans des modèles qui ne se produisent jamais jusqu'à ce qu'ils en aient besoin - et vous pouvez même créer des arbres d'analyse qui développent les récursions imbriquées si vous remplissez les modèles avec une aliaschaîne. C'est le moyen le plus rapide que j'ai trouvé pour obtenir un shell pour faire des choses comme la traduction de caractères et pour échanger des caractères contre des valeurs d'octets. cela peut être assez rapide - C-Locale ASCII + <> octal le pire des cas est 7 extensions de modèle POSIX de base.
mikeserv
1

Ce n'est pas très joli mais vous pouvez l'utiliser:

 #!/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
la source
Vous voudrez peut-être "canoniser" le nombre avec $ (($ number)) pour couvrir des nombres comme "001" ou "0x99" ... Cela couvrirait également pour "12" et "12 + 12" qui peuvent ou peuvent pas souhaitable.
Stéphane Chazelas