case + как реализовать равный или меньший или больший в случае синтаксиса case


9

Моя цель - проверить диапазон числа с помощью (только с case+ esac) и распечатать диапазон. Так, например:

  • Если число находится в диапазоне от 0 до 80, выведите >=0<=80
  • Если число находится между 81 и 100, напечатайте >=81<=100
  • и т.п.

Проблема с моим сценарием, приведенным ниже, печатается только в том >=0<=90случае, если число от 0 до 9. Как исправить мой сценарий, чтобы он выводил правильный вывод в соответствии с диапазоном номеров?

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

Ответы:


6

caseпредназначен только для сопоставления с образцом, он не будет выполнять арифметическую оценку (за исключением, может быть, если учесть расширенный оператор сопоставления zshс <x-y>образцом). [...]Только , чтобы соответствовать один символ (или упорядочения элемент в некоторых вариантах осуществления) на основе набора , указанного внутри. Так, например , [0-80]будет соответствовать один символ , если это один из 0к 8или 0(то есть, один из 0, 1, 2, 3, 4, 5, 6, 7, 8).

Вы можете сопоставить числа с шаблонами, такими как:

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

Но вы можете легко увидеть, что это не тот инструмент.

[...]Соответствует один символ против списка заданных символов, так [121-300]спичек для любого символа, либо 1, 2, 1 до 3, 0 или 0, так что это так же , как [0-3]и [0123].

Использование:

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

Другой способ использования caseбудет выглядеть так:

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

Или используйте троичный оператор ( 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...

Или как @mikeserv, мыслить нестандартно, обратить caseлогику и соответствие 1со значением этих арифметических сравнений .


1
+1, посмотрим if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Меньше печатать, меньше подвержен ошибкам.
Петер

@peterph Это также занимает больше времени для запуска.
Кен Шарп

4

На самом деле это действительно легко сделать. Дело в том, caseчто он всегда будет расширяться только настолько, насколько необходимо, чтобы найти первое совпадение с шаблоном. Это специальное поведение. И поэтому вы можете просто установить его с известной строкой и оценить расширения шаблонов.

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

caseникогда не будет расширять эти паттерны больше, чем нужно, чтобы найти ведущую 1 в паттерне. Это особенно важно при работе с пользовательским вводом, потому что это означает, что вы можете безопасно проверить содержимое, $numberпрежде чем пытаться поместить его в арифметический контекст расширения, в том же операторе case, в котором вы фактически поместили его в математическое расширение.


👍 Мне нравится, как вы думаете вне / вокруг коробки.
Стефан Шазелас

@ StéphaneChazelas - мне нравится case. есть некоторые интересные вещи, которые вы можете делать с $((математикой ))и case- особенно с окружающими назначениями в шаблонах, которые никогда не происходят, пока в них нет необходимости - и вы даже можете создавать деревья разбора, которые расширяют вложенные рекурсии, если вы заполняете шаблоны aliasцепочкой. Это самый быстрый способ, который я нашел, чтобы заставить оболочку выполнять такие вещи, как перевод символов и менять символы на значения байтов. это может быть довольно быстро - C-Locale ASCII + <> восьмеричный худший случай - это 7 базовых расширений паттерна POSIX.
mikeserv

1

Это не очень хорошо, но вы можете использовать это:

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

Возможно, вы захотите «канонизировать» число с помощью $ (($ number)), чтобы покрыть такие числа, как «001» или «0x99» ... Это также относится к «12» и «12 + 12», которые могут или могут быть не желательно
Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.