Bash、検索文字列に一致するコンテンツを見つける機能

Bash、検索文字列に一致するコンテンツを見つける機能

特定の検索文字列に対してbashで定義されているすべての関数を検索したいと思います。以下は始まりですが、すべての用語を削除したいと思います。いいえ次の行の後にはスペースが続きます(つまり、$1関数の本文に見つからない項目を削除します)。

fu() { declare -f | grep -e \(\) -e $1; }

たとえば、次の出力は次のようになります。

...
tt ()
untargz ()
urlfix ()
ver ()
    [ -f /etc/lsb-release ] && RELEASE="$(cat /etc/lsb-release | grep DESCRIPTION | sed 's/^.*=//g' | sed 's/\"//g') ";
vi. ()
vi.su ()
...

に減少

...
ver ()
    [ -f /etc/lsb-release ] && RELEASE="$(cat /etc/lsb-release | grep DESCRIPTION | sed 's/^.*=//g' | sed 's/\"//g') ";
...

(可能な場合)より良いアプローチは、各一致関数を全体的に決定して表示できることです。

私の考えはおおよそ次の通りです。

  • 本文から関数名と検索文字列を収集します(関数名は常に一致の前の行にある単一の単語で始まり、その後にスペースが続き、最後の行が続きます)。^各関数の名前を指定または再実行します。しかし、今回はその名前を取得し、その後のすべての項目をfrom(ここで1行の各項目と一致させます。grep / awk / sedが知識のあるもの)。()$command -Vdeclare -f{}{}^

fu awk最終結果が実行され、関数本体に含まれる各関数の定義が表示されます。awk

答え1

awkパイプの受信側で次のコマンドを思い出しました。

declare -f | awk -v srch="pattern" 'NF==2 && $2=="()"{if (m) print buf; buf=""; m=0}
                                    buf && index($0,srch){m=1}
                                    {buf=buf ORS $0}
                                    END{if (m) print buf}'

bufアイデアは、出力を解析するときに各関数の宣言と本文をバッファに格納しますが、declare -f検索文字列が見つかった場合にのみバッファを印刷することです。

  • ()2番目のフィールドが2つのフィールド(=スペースで区切られた「単語」)のみを含む行を検出すると、プログラムは新しい関数定義の開始を認識します。以前の関数を解析するときに一致するものが見つかると(フラグ1で示されている)、mバッファが印刷されます。bufバッファとフラグの両方がリセットされます。
  • awkクエリはプログラムに変数として渡されますsrch。現在の行で見つかると(index関数はゼロ以外の結果を返します)、mフラグは1に設定されますが、関数宣言が開始される行にない場合にのみ(空でないとマークされている)buf、そうでない場合は一致する機能があります名前重要です。
  • 各行はバッファに追加され、buf出力レコードの区切り文字(デフォルトは改行)で前のORS内容と区別されます。
  • 最後に一致する項目が見つかった場合は別のチェックが実行され、見つかった場合はバッファが印刷されます。この確認がなければ、最後の関数定義は考慮されません。

ノート

index()このプログラムで使用される関数は完全な文字列検索を実行しますawk。正規表現の一致に基づいて検索するには、条件を次に変更する必要があります。

index($0,srch)

到着

$0~srch

(しかし、いつものように、正規表現に特別な意味を持つ文字を含む文字列を検索する方が面倒です。)

答え2

で同じことを行い、zsh本文がパターンに一致する関数の関数定義を取得するには、次のようにします。

typeset -f '<dummy>' ${(k)functions[(R)pattern]}

<dummy>一致する機能がない場合を隠すために使用されます。またはよりきれいな方法:

() { (($#)) && typeset -f -- "$@"; } ${(k)functions[(R)pattern]}

では、bash次のことができます。

compgen -A function | (
  ret=1
  while IFS= read -r fn; do
    def=$(typeset -f -- "$fn") &&
      [[ ${def#*'()'} = pattern ]] &&
      printf '%s\n' "$def" &&
      ret=0
  done
  exit "$ret"
)

ただし、関数名のリストを一度に1バイトずつ読み込み(入力がパイプであるreadため)、各関数がプロセスをフォークするため、これはかなり非効率的です。次の方法で最適化できます。

compgen -A function | (
  readarray -t functions
  for fn in "${functions[@]}"; do
    typeset -f -- "$fn" && printf '\0'
  done
) | (
  ret=1
  readarray -td '' fn_definitions
  for def in "${fn_definitions[@]}"; do
    [[ ${def#*'()'} = pattern ]] &&
      printf '%s\n' "$def" &&
      ret=0
  done
  exit "$ret"
)

readarray対照的にread、とにかくすべての入力を消費するため、入力を一度に 1 バイトずつ読み込む必要はありません。その文字はとにかく関数定義/名前または変数の内容に格納できないため、関数定義をNUL文字で区切ってください。bashその後、それを使用して(bash-4.4+が必要です)それを配列に分解できます。zshreadarray -td ''

()これにより、出力で各関数定義が始まる場所を推測するために(関数の本文内でも発生する可能性がある)経験的な方法の存在に頼る必要がなくなりますtypeset -f

関連情報