【问题标题】:How to rewrite a Awk script to process several files instead of one如何重写 Awk 脚本来处理多个文件而不是一个文件
【发布时间】:2014-11-22 06:42:54
【问题描述】:

我正在编写一个报告工具,它处理某个应用程序的源文件并生成一个包含两列的报告表,一列包含文件名,另一列包含单词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 脚​​本,使其支持多个输入文件,而不仅仅是一个 - 这样我就可以删除 xargs-n 1 参数。我能想到的唯一解决方案涉及大量记账,因为我们需要跟踪 FILENAMEEND 事件的更改以捕获每个文件结束事件

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,正在寻找与此工具兼容的解决方案。

【问题讨论】:

    标签: awk report xargs


    【解决方案1】:

    这适用于任何现代 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 循环遍历它并为 END 部分填充不同的数组加工)。如果您有空文件,其他任何操作都会失败。

    【讨论】:

    • 的改进!感谢您的洞察力!
    • +1 用于避免重复并确保无输入 = 无输出
    【解决方案2】:

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

    【讨论】:

    • 这与我自己的解决方案非常相似,因为它需要跟踪FILENAME 的变化并监控END……所以似乎没有真正的方法可以避免这种情况。对于-0 的事情,我们一直在将我们的人员在源文件名中使用空格发送到动物标本剥制部门,但是这句话可能对普通读者有用。
    • 我认为您的 awk 版本没有任何方法可以避免这种情况。如果您使用的是 GNU awk,我会建议使用 ENDFILE...我刚刚对其进行了编辑以将长度缩短一点。我敢肯定还有更多可以做的事情,尽管可能会以牺牲可读性为代价。
    • 请注意,deprecated 是一个变量,因此您不能在正则表达式文字中使用它。
    • @Ed 谢谢,我已经编辑过了。从您上面的评论中得到的一点是,如果没有文件传递给它,它仍然会产生输出。
    • @TomFenech 它不会为传递给它的每个空文件生成任何输出,如果最后一个文件为空,它将为倒数第二个文件生成两次报告,等等。 . 不幸的是,如果没有 gawk+ENDFILE,你就无法在 END 部分中的文件没有循环的情况下完成这种类型的工作,这就是 gawk 有 ENDFILE 的原因。
    猜你喜欢
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多