我必须处理一些大文件并在每一行上运行多个测试。我目前正在使用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的循环每行多次传递。
提前致谢。
答案 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文件中。我也改变了你的第二次测试,所以并非总是如此。