case + como implementar igual ou menor ou maior na sintaxe do caso

9

Meu objetivo é verificar um intervalo de número com (apenas com case+ esac) e imprimir o intervalo. Então, por exemplo:

  • Se o número estiver entre 0 e 80, imprima >=0<=80
  • Se o número estiver entre 81 e 100, imprima >=81<=100
  • etc.

O problema com o meu script abaixo é impresso apenas >=0<=90se o número estiver entre 0 e 9. Como corrigir o meu script, para que ele imprima a saída correta de acordo com o intervalo 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
fonte

Respostas:

6

caseé apenas para correspondência de padrões, não fará avaliação aritmética (exceto, talvez, se você considerar zsho <x-y>operador de correspondência de padrões estendida). O [...]é apenas para corresponder a um caractere (ou elemento de intercalação em algumas implementações) com base no conjunto especificado em. Assim, por exemplo [0-80]iria corresponder um personagem, se ele é um dos 0que 8ou 0(isto é, de 0, 1, 2, 3, 4, 5, 6, 7, 8).

Você pode combinar números com padrões 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

Mas você pode ver facilmente que não é a ferramenta certa.

Ele [...]corresponde a um caractere em relação à lista de caracteres especificados, portanto [121-300]corresponde a qualquer caractere que seja 1, 2, 1 a 3, 0 ou 0, portanto é o mesmo que [0-3]ou [0123].

Usar:

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

Outra maneira de usar caseseria 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

Ou use o operador ternário ( 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 como @mikeserv, pense fora da caixa, inverta a caselógica e combine 1com o valor dessas comparações aritméticas .

Stéphane Chazelas
fonte
1
+1, considere if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Menos digitação, menos propenso a erros.
Peterph
@ Peter Também leva mais tempo para ser executado.
Ken afiada
4

Na verdade, isso é realmente fácil de fazer. A questão caseé que ele sempre será expandido apenas o necessário para encontrar a primeira correspondência contra um padrão. Esse é o comportamento especificado. E assim você pode configurá-lo com uma string conhecida e avaliar as expansões dos padrões.

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á mais desses padrões do que o necessário para encontrar um 1 inicial no padrão. Isso é especialmente importante ao trabalhar com a entrada do usuário, porque significa que você pode verificar com segurança o conteúdo de $numberantes de tentar colocá-lo em um contexto de expansão aritmética na mesma instrução de caso em que você realmente o coloca em uma expansão matemática.

mikeserv
fonte
👍 Gosto da maneira como você pensa fora / em volta da caixa.
Stéphane Chazelas
@ StéphaneChazelas - eu gosto case. existem algumas coisas legais que você pode fazer com $((matemática ))e case- especialmente tarefas circunvizinhas em padrões que nunca acontecem até que precisem - e você pode até criar árvores de análise que expandem recursões aninhadas se você preencher os padrões com uma aliascadeia. é a maneira mais rápida encontrada para obter um shell para fazer coisas como tradução de caracteres e trocar caracteres por valores de bytes. pode ser bem rápido - o pior caso C-Locale ASCII + <> octal é 7 expansões básicas de padrão POSIX.
mikeserv
1

Isso não é muito bom, mas você pode usar isso:

 #!/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
fonte
Você pode "canonificar" o número com $ ((número $)) para cobrir números como "001" ou "0x99" ... Isso também abrangeria "12" e "12 + 12", que podem ou podem não é desejável.
Stéphane Chazelas