awkからフルパイプ入力を読む

awkからフルパイプ入力を読む

私が知っている限り、awkは2つの方法で使用できます。ファイルのリストをパラメータとして渡すか、パイプラインで使用できます。ファイルリストをパラメータとして使用したので、BEGINandENDブロックはすべてのファイルに対して一度だけ実行されます。しかし、問題があります。ファイルをawkに渡すには、まずファイルの復号化が必要です。そのため、次のようにパイプラインを設定しました。

find . -name "*.gpg" -exec sh -c "gpg -d {} | awk -f process.awk" \;

これですべてのファイルが実行されBEGINブロックENDされますが、process.awkこれは私が望むものではありません。ブロックが一度だけ実行されるようにawkに渡されたファイルを復号化する方法はありますか?更新:ファイル名も必要なので、process.awkファイルの内容を別々にインポートする方が良いと思いました。ただし、これは見つかったすべてのファイルに対して一度だけ実行する必要があるという要件に違反します。そうですかBEGINEND

ベストアンサー1

ファイル名に改行文字が含まれていないとします。

while IFS= read -r fname; do
    gpg -d "$fname"
done < <(find . -name '*.gpg') |
awk -f process.awk

今話していることを行い、各ファイル名をawkに渡すには、最も簡単な方法は次のとおりです(bash 4. *を使用またはreadarray埋めるmapfileループを作成するとしますfnames[])。

readarray -t fnames < <(find . -name '*.gpg')
for fname in "${fnames[@]}"; do
    gpg -d "$fname" |
    awk -v fname="$fname" -v tot="${#fnames[@]}" -v nr="$((++nr))" -f process.awk
done

これにより、各ファイル名があり、コードがfnameセクションで実行されているかどうかをテストできます。nr==1BEGINnr==totEND

BEGIN {
    if (nr==1) {
        do BEGIN stuff
    }
}
{ do common stuff }
END {
    if (nr==tot) {
        do END stuff
    }
}

あるいは、一時ファイル用のスペースがある場合は、呼び出しを繰り返して、すべての出力をgpg同じ名前のファイルの一時ディレクトリに書き込んでから、変更せずに各ファイルに対してawkを呼び出すこともできますprocess.awk

tmpdir=$(mktemp -d) &&
while IFS= read -r fname; do
    gpg -d "$fname" > "$tmpdir"/"$fname"
done < <(find . -name '*.gpg') &&
awk -f process.awk "$tmpdir"/* &&
rm -rf "$tmpdir"

おすすめ記事