我有以下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循环中使用"太多的参数"错误,由于文件数量很大。 谁能想到解决方案? 谢谢 ! 罗伊
答案 0 :(得分:4)
您的代码中最大的瓶颈是您正在运行单独的mv
进程(它只是系统调用的包装器)来重命名每个文件。假设您有100,000个文件,其中20,000个需要重命名。您的原始代码需要120,000个进程,每个文件一个grep
,每个重命名一个mv
。 (忽略对find
和xargs
的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
已经得到了很好的优化,这样做的代码虽然不是非常复杂,但仍然比你在单行中写得更舒服。这应该是速度和简单性之间的良好平衡。