据我了解,awk 可以通过两种方式使用。您可以将文件列表作为参数传递给它,也可以在管道中使用它。我尝试将它与文件列表作为参数一起使用,因此BEGIN
和END
块仅对所有文件执行一次。但我有一个问题,我的文件需要先解密,然后才能将它们传递给 awk。所以我对管道进行了以下设置:
find . -name "*.gpg" -exec sh -c "gpg -d {} | awk -f process.awk" \;
现在,每个文件都会执行BEGIN
和END
块process.awk
,这不是我想要的。有没有办法解密传递给 awk 的文件,以便这些块只执行一次?更新:因为我还需要文件名,所以process.awk
我认为最好单独获取文件内容,但是这样就违反了我对所有找到的文件执行一次的要求,对吗BEGIN
?END
答案1
假设您的文件名不包含换行符:
while IFS= read -r fname; do
gpg -d "$fname"
done < <(find . -name '*.gpg') |
awk -f process.awk
要执行您现在所说的操作并将每个文件名传递给 awk,最简单的事情可能是(假设 bash 4.* forreadarray
或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==1
BEGIN
nr==tot
END
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"