awk在每一行传递多次

时间:2014-11-04 14:35:10

标签: bash awk

我必须处理一些大文件并在每一行上运行多个测试。我目前正在使用awk来运行单独的测试,并使用" while-read-line-do"循环将每行传递给十几个这样的awk命令,这些命令测试各个列以验证其内容并记录错误。传递所有测试的行将附加到.VALID文件。

我目前面临的问题是这个过程非常缓慢。从阅读网上和StackOverflow上的许多其他帖子后我收集的内容来看,主要的罪魁祸首是" while-read-line-do"循环,它不会将文件(每个约100K行)借入内存。

我希望有人可以帮助我找到更好的方法来实现,这样我就可以获得类似awk的表现。这是我的代码的简化版本:

while read line || [[ -n "$line" ]];do
    echo $line | awk -F\; '{
        if ( ($3 != "P") && ($3 != "0") ) {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"};
            else print $0 >> "INPUT_FILE.OK";
        }'
    echo $line | awk -F\; '{
        if ( ($7 < 10) || ($7 > 3) ) {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"};
            else print $0 >> "INPUT_FILE.OK";
        }'  
    echo $line | awk -F\; '{
        if ( ($36 < 0) || ($36 > 1000) ) {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"};
            else print $0 >> "INPUT_FILE.OK";
        }'
done < INPUT_FILE.txt

理想情况下,我试图提出一种解决方案,允许我使用基于awk的循环每行多次传递。

提前致谢。

1 个答案:

答案 0 :(得分:3)

绝对没有必要将线路逐一传递给awk; awk代表您逐行处理文件。您的答案中的代码可以简化为:

awk -F\; '($3!="P"&&$3!="0")||($7<10||$7>3)||($36<0||$36>1000)
          {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"; next}
          {print >> "INPUT_FILE.OK"}' INPUT_FILE.txt

我怀疑这会更快。

awk程序的结构是condition { action },因此很少需要使用if / else。相反,您可以在next分支中使用if,这意味着awk将跳到下一行而不是运行第二个块。

此输出略有不同,因为错误日志中不会复制多个测试失败的行。我认为这没关系,因为你的每张支票的输出都是一样的。

为了进一步改善性能,你可以考虑按照可能性的顺序安排测试,因为这意味着这种情况更容易发生短路。

请注意,在awk中,>>>在shell中具有不同的含义。 >将意味着awk第一次创建一个新文件并在连续写入时附加到它,因此您可能希望使用它。如果该文件尚不存在,那么它并不重要。

正如评论中所提到的,看起来$7<10||$7>3存在逻辑错误,因为这总是正确的。也许你让><混淆了?

如果要为每个错误编写单独的输出,可以稍微更改结构:

awk -F\; '{f=0}
          $3!="P"&&$3!="0" {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"; f=1}
          $7<3||$7>10      {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"; f=1}
          $36<0||$36>1000  {print $0 "; ERROR ;" >> "INPUT_FILE.ERRORS"; f=1}
          !f {print >> "INPUT_FILE.OK"}' INPUT_FILE.txt

每个测试都是单独进行的,如果任何测试都为真,则f设置为true。如果对该行进行了所有测试后f仍然为假,则会将其打印到OK文件中。我也改变了你的第二次测试,所以并非总是如此。