動的ケース記述の生成

動的ケース記述の生成

やや長いケース説明があります(一致するパターンは約150個です)。

case "$line" in
  pattern1) ret=result1;;
  pattern2) ret=result2;;
  pattern3) ret=result3;;
   ...
   ...
esac

ただし、スキーマは実際にはユーザーが変更できるデータなので、これは適切ではありません。そのため、新しいパターンが必要なときはいつでもCaseステートメントを変更する必要があります。

私がやりたいことは、次のような別々のテキストファイルを作成することです。

pattern1=result1
pattern2=result2
pattern3=result3
...
...

テキストファイルを読み、ケースの説明を作成するために使用されます。これには、新しいパターンが必要なときはいつでもコードを変更する必要がないという利点があります。

テキストファイルの入力に基づいてケースステートメントを自動的に生成する方法はありますか?他のオプションも開いていますが、まだ一致させる入力ラインが多いため、入力ラインを非常に迅速にパターン一致させることができるソリューションを探しています。

答え1

動的ケースの説明生成はやや奇妙なことですが…そうです。可能です。

私は以下のようにします。 (私が探索した場合みんな私が考えることができる他の方法は次のとおりです。

. <( awk -F= 'BEGIN { print "case \"$line\" in" }
                    { print $1 ") ret=" $2 ";;" }
              END   { print "esac" }' patternfile )

patternfile以下を含む場合

pattern1=result1
pattern2=result2
pattern3=result3

コマンドawk自体が出力されます。

case "$line" in
pattern1) ret=result1;;
pattern2) ret=result2;;
pattern3) ret=result3;;
esac

この. <( awk ... )構成では、スクリプトファイルに大文字と小文字のスイッチを直接作成するかのように、スクリプトにソースが入力されます。


これはシェルスクリプトの「動的ケース記述の生成」に対する答えです。でも、やる予定なのでループ内で、上記のアプローチは非常に悪いアプローチです。awk10,000回実行することがpatternfile可能なパフォーマンス上の利点よりも重要になるからです。

代わりに、ケーススイッチを生成するコードジェネレータを書くことをお勧めします。関数定義内で- 完全な関数定義を作成してインポートします。つまり、次のようになります。

# At the top of your script file:
. <( awk -F= 'BEGIN { print "my_case_switch_function() {"
                      print "case \"$1\" in" }
                    { print $1 ") ret=" $2 ";;" }
              END   { print "esac"
                      print "}"   }' patternfile )

# Within your while loop (or wherever else you want):
my_case_switch_function "$line"

caseこれにより、結果スイッチを一度だけ処理してから繰り返し(必要に応じて最大10,000回)再使用できますpatternfile。したがって、パフォーマンスはpatternfile手動で大文字と小文字のスイッチを作成し、それをスクリプトにハードコードしたのと同じです(awk150行のファイルで一度実行する小さなオーバーヘッドを除いて、10,000行のファイルを処理するのと比較すると無視できます)。


しかし、再度強調する必要があるのは、シェルスクリプトケーススイッチは次のとおりです。いいえ10,000行のファイルを1行ずつ処理するツールです。したがって、このソリューションはケーススイッチをハードコーディングすることで達成できるパフォーマンスに非常に近いですが、それでも遅くなる可能性があります。

見積もりスティーブン・チャジェラス:

前述したように、コマンドを実行するにはコストがかかります。命令が組み込まれていないとコストは膨大ですが、組み込まれていてもコストは途方もなくなります。

シェルはこのように動作するようには設計されておらず、高性能プログラミング言語であるとも主張していません。彼らはそうではありません。彼らは単にコマンドラインソルバーです。したがって、これに関して最適化はほとんど行われなかった。

答え2

私の考えでは、これ XY質問。実際に動的ステートメントを生成する必要はありませんcase。ファイルを単純なKey-Valueストアとして使用したいだけです。 1つの解決策は、検索ファイルを使用してgrepから次のように値を抽出することですcut

ret=$(grep "^$line" file.txt | cut -d = -f 2)

答え3

次のスキーマファイルを使用します。

a*c=abc
def=def

=そして行ごとに1つしかないとします。

#! /bin/bash

line=abc
while IFS= read -r patternline; do
        [[ $patternline =~ .=. ]] || continue
        pattern="${patternline%=*}"
        result="${patternline##*=}"
        if [[ "$line" == $pattern ]]; then
                ret="$result"
                echo "$patternline"
                break
        fi
done <patterns.txt

複数行を確認する必要がある場合は、ファイルの内容を配列に読み込みます。

#! /bin/bash

patterns_file="patterns.txt"

patterns=()
results=()
patternindex=0


### BEGIN: init
while IFS= read -r patternline; do
        [[ $patternline =~ .=. ]] || continue
        patterns[patternindex]="${patternline%=*}"
        results[patternindex]="${patternline##*=}"
        ((patternindex++))
done <"$patterns_file"
pattern_count="$patternindex"
### END:   init

patterncheck () {
        local line="$1" i=0 pattern= result=
        for((i=0;i<pattern_count;i++)); do
                pattern="${patterns[i]}"
                result="${results[i]}"
                if [[ "$line" == $pattern ]]; then
                        ret="$result"
                        echo "$line"
                        break
                fi
        done
} # patterncheck ()

# tests

patterncheck abc
patterncheck def

関連情報