case + cómo implementar igual o menor o mayor en la sintaxis de mayúsculas y minúsculas

9

Mi objetivo es verificar un rango de números con (solo con case+ esac) e imprimir el rango. Así por ejemplo:

  • Si el número está entre 0 y 80, imprima >=0<=80
  • Si el número está entre 81 y 100, imprima >=81<=100
  • etc.

El problema con mi secuencia de comandos a continuación solo se imprime >=0<=90si el número entre 0 y 9. ¿Cómo corregir mi secuencia de comandos, para que imprima la salida correcta de acuerdo con el rango de números?

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

Respuestas:

6

casees solo para la coincidencia de patrones, no realizará una evaluación aritmética (excepto tal vez si considera zshel <x-y>operador de coincidencia de patrones extendido). El [...]es solo para hacer coincidir un carácter (o elemento de clasificación en algunas implementaciones) en función del conjunto especificado dentro. Así, por ejemplo [0-80]se correspondería con un carácter si es uno de 0a 8o 0(es decir, uno de 0, 1, 2, 3, 4, 5, 6, 7, 8).

Puede unir números con patrones como:

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

Pero puede ver fácilmente que no es la herramienta adecuada.

La [...]partidos un personaje con la lista de caracteres especificados, por lo [121-300]coincidencias para cualquier carácter que puede ser 1, 2, 1 a 3, 0 ó 0, por lo que es lo mismo que [0-3]o [0123].

Utilizar:

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

Otra forma de uso casesería como:

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

O use el operador ternario ( 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...

O como @mikeserv, piense fuera de la caja, invierta la caselógica y1 haga coincidir el valor de esas comparaciones aritméticas .

Stéphane Chazelas
fuente
1
+1, considéralo if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Menos tipeo, menos propenso a errores.
Peter
@peterph También lleva más tiempo ejecutarlo.
Ken Sharp
4

En realidad, esto es realmente fácil de hacer. La cuestión casees que siempre se expandirá tanto como sea necesario para encontrar la primera coincidencia con un patrón. Ese es el comportamiento especificado. Y así puede configurarlo con una cadena conocida y evaluar las expansiones de los patrones.

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

casenunca expandirá más de esos patrones de lo necesario para encontrar un 1 en el patrón. Esto es especialmente importante cuando se trabaja con la entrada del usuario, porque significa que puede verificar de forma segura el contenido de $numberantes de intentar ponerlo en un contexto de expansión aritmética en la misma declaración de caso en el que realmente lo coloca en una expansión matemática.

mikeserv
fuente
👍 Me gusta cómo piensas fuera / alrededor de la caja.
Stéphane Chazelas
@ StéphaneChazelas - me gusta case. hay algunas cosas geniales que puedes hacer con las $((matemáticas ))y case, especialmente las asignaciones circundantes en patrones que nunca suceden hasta que lo necesiten, e incluso puedes construir árboles de análisis que expanden las recursiones anidadas si llenas los patrones con una aliascadena. es la forma más rápida que he encontrado para obtener un shell para hacer cosas como la traducción de caracteres e intercambiar caracteres por valores de bytes. puede ser bastante rápido: C-Locale ASCII + <> octal, el peor de los casos es 7 expansiones de patrones POSIX básicos.
mikeserv
1

Esto no es muy bueno, pero puedes usar esto:

 #!/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
fuente
Es posible que desee "canonificar" el número con $ (($ número)) para cubrir números como "001" o "0x99" ... Eso también cubriría "12" y "12 + 12" que pueden o pueden No sea deseable.
Stéphane Chazelas