我正在编写一个报告工具,它处理某些应用程序的源文件并生成一个包含两列的报告表,一列包含文件名,另一列包含单词TODO
(如果文件包含一个调用)除了某些已弃用的函数deprecated_function
和DONE
。
我使用awk
来准备此报告,我的shell脚本看起来像
report()
{
find . -type f -name '*.c' \
| xargs -n 1 awk -v deprecated="$1" '
BEGIN { status = "DONE" }
$0 ~ deprecated{ status = "TODO" }
END {
printf("%s|%s\n", FILENAME, status)
}'
}
report "deprecated_function"
此脚本的输出类似于
./plop-plop.c|DONE
./fizz-boum.c|TODO
这很好但我想重写awk
脚本以便它支持多个输入文件而不只是一个 - 这样我就可以将-n 1
参数删除到xargs
。我能想出的唯一解决方案涉及大量记账,因为我们需要跟踪FILENAME
和END
事件的变化以捕获每个文件结束事件。 / p>
awk -v deprecated="$1" '
BEGIN { status = "DONE" }
oldfilename && (oldfilename != FILENAME) {
printf("%s|%s\n", oldfilename, status);
status = DONE;
oldfilename = FILENAME;
}
$0 ~ deprecated{ status = "TODO" }
END {
printf("%s|%s\n", FILENAME, status)
}'
也许有更清洁,更短的方式来解决这个问题。
我正在使用FreeBSD' awk
,我正在寻找与此工具兼容的解决方案。
答案 0 :(得分:3)
这适用于任何现代的awk:
awk -v deprecated="$1" -v OFS='|' '
$0 ~ deprecated{ dep[FILENAME] }
END {
for (i=1;i<ARGC;i++)
print ARGV[i], (ARGV[i] in dep ? "TODO" : "DONE")
}
' file1 file2 ...
任何时候你需要为所有文件生成一个报告并且没有ENDFILE的GNU awk,你必须在END部分循环遍历ARGV [](或者在BEGIN中循环遍历它并填充不同的数组结束部分处理)。如果您有空文件,其他任何内容都将失败。
答案 1 :(得分:1)
你的awk脚本可能是这样的:
awk -v deprecated="$1" '
FNR==1 {if(file) print file "|" (f?"TODO":"DONE"); file=FILENAME; f=0}
$0 ~ deprecated {f=1}
END {print file "|" (f?"TODO":"DONE")}' file1.c file2.c # etc.
逻辑与您的程序非常相似,所以希望它一目了然。 FNR
是当前文件的记录号,我用它来检测新文件的开头。不可否认,END
区块有一些重复,但我不认为这是一个大问题。如果你愿意,你总是可以使用一个函数。
测试出来:
$ cat f1.c
int deprecated_function()
{
// some deprecated stuff
}
$ cat f2.c
int good_function()
{
// some good stuff
}
$ find -name "f?.c" -print0 | xargs -0 awk -v deprecated="deprecated" 'FNR==1 {if(file) print file "|" (f?"TODO":"DONE"); file=FILENAME; f=0} $0 ~ deprecated {f=1} END {print file "|" (f?"TODO":"DONE")}'
./f2.c|DONE
./f1.c|TODO
我使用-print0
和-0
切换到xargs,以便两个程序的工作文件名用空字节“\ 0”而不是空格分隔。这意味着您不会遇到文件名中的空格问题。