如何重写Awk脚本来处理多个文件而不是一个

时间:2014-09-27 09:47:48

标签: awk report xargs

我正在编写一个报告工具,它处理某些应用程序的源文件并生成一个包含两列的报告表,一列包含文件名,另一列包含单词TODO(如果文件包含一个调用)除了某些已弃用的函数deprecated_functionDONE

我使用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。我能想出的唯一解决方案涉及大量记账,因为我们需要跟踪FILENAMEEND事件的变化以捕获每个文件结束事件。 / 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,我正在寻找与此工具兼容的解决方案。

2 个答案:

答案 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”而不是空格分隔。这意味着您不会遇到文件名中的空格问题。