ケース+ケース構文で等しいかそれ以下またはそれ以上を実装する方法


9

私の目標は、数値の範囲を(case+ のみでesac)確認し、その範囲を印刷することです。だから例えば:

  • 数値が0〜80の場合、印刷 >=0<=80
  • 数値が81から100の間の場合、印刷 >=81<=100

以下の私のスクリプトの問題は、>=0<=900から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>拡張パターンマッチング演算子を検討する場合を除いて)。[...]一致するだけで1つの文字(または照合要素内に指定されたセットに基づいて、いくつかの実装では)。したがって、たとえば、to またはor (つまり、0、1、2、3、4、5、6、7、8 のいずれか)であれば、1つの文字[0-80]と一致します。080

次のようなパターンで数字を照合できます。

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

しかし、それが適切なツールではないことが簡単にわかります。

[...]試合1つの指定された文字のリストに対して文字なので、[121-300]3のいずれか1である任意の文字に一致したもの、2、1、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 2013

@peterph実行にも時間がかかります。
Ken Sharp

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ステートメントで算術拡張コンテキストに配置しようとする前に、その内容を安全に検証できることを意味します。


box私はあなたが箱の外/周りで考える方法が好きです。
ステファンChazelas

@StéphaneChazelas-私は好きcaseです。theresのいくつかのクールなものあなたが行うことができます$((数学))case-あなたがしてパターンを取り込む場合でも、ネストされた再帰を展開する解析木を構築することができます-特に彼らが必要とするまで決して起こらないことをパターンに割り当てを取り巻くaliasチェーン。これは、シェルに文字変換などを実行させ、文字をバイト値に交換するために見つかった最も速い方法です。それはかなり高速になる可能性があります-CロケールASCII + <> 8進数最悪のケースは7つの基本的なPOSIXパターン展開です。
mikeserv 2015年

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」もカバーする可能性があります。望ましくない。
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.