多线程Bash in while循环

时间:2016-06-27 10:29:30

标签: linux bash for-loop while-loop grep

我有以下Bash one liner,它应遍历名为* .xml的文件夹中的所有文件,检查它们是否具有以下字符串,如果没有,则将它们重命名为$ .empty

find -name '*.xml'   | xargs -I{} grep -LZ "state=\"open\"" {} | while IFS= read -rd '' x; do mv "$x" "$x".empty ; done 

此过程非常缓慢,并且在包含超过100k文件的文件夹中运行此脚本时,需要15分钟才能完成。 我找不到让这个过程多线程运行的方法。 请注意,在for循环中使用"太多的参数"错误,由于文件数量很大。 谁能想到解决方案? 谢谢 ! 罗伊

1 个答案:

答案 0 :(得分:4)

您的代码中最大的瓶颈是您正在运行单独的mv进程(它只是系统调用的包装器)来重命名每个文件。假设您有100,000个文件,其中20,000个需要重命名。您的原始代码需要120,000个进程,每个文件一个grep,每个重命名一个mv。 (忽略对findxargs的2次调用。)

更好的方法是使用一种语言,而不是直接访问系统调用。这是一个简单的Perl示例:

find -name '*.xml' | xargs -I{} grep -LZ "state=\"open\"" {} |
  perl -n0e 'rename("$_", "$_.empty")'

只需拨打一次mv即可将perl的两次来电替换为grep

另一个瓶颈是为每个文件运行一个grep进程。相反,您希望每次都将尽可能多的文件传递给xargs。这里不需要-exec;请改用find主要find -name '*.xml' -exec grep -LZ "state=\"open\"" {} + | perl -n0e 'rename("$_", "$_.empty")'

grep

您收到的参数错误太多是基于总参数长度。假设限制为4096,并且您的XML文件的平均名称长度为20个字符。这意味着您应该能够在每次调用-exec ... +时传递200多个文件。 grep主要负责向每个grep调用传递尽可能多的文件,因此此代码最多需要100,000 {200 = 500次调用grep,这是一项巨大的改进。

根据文件的大小,在Perl进程中读取每个文件以检查要匹配的字符串可能会更快。但是,extension=zmq.so 已经得到了很好的优化,这样做的代码虽然不是非常复杂,但仍然比你在单行中写得更舒服。这应该是速度和简单性之间的良好平衡。