相关文章推荐
留胡子的电影票  ·  Install and Set Up ...·  1 周前    · 
瘦瘦的马克杯  ·  4020电子书 - 百度·  2 月前    · 
买醉的钥匙  ·  四川省文化和旅游厅·  5 月前    · 
淡定的玉米  ·  狄昂·华薇克_百度百科·  1 年前    · 
失恋的领带  ·  北黑铁路_百度百科·  1 年前    · 

在while循环中的多线程Bash

1 人不认可

我有以下的Bash单行程序,它应该遍历文件夹中所有名为*.xml的文件,检查它们是否有以下的字符串,如果没有,就把它们重命名为$.empty。

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

这个过程非常慢,当在有超过10万个文件的文件夹中运行这个脚本时,它需要远远超过15分钟才能完成。 我找不到让这个过程多线程运行的方法。 请注意,由于文件数量太多,在for循环中会出现 "参数太多 "的错误。 谁能想到解决办法?

5 个评论
为什么你认为多线程会更快? 你的CPU核心是100%吗? 你的磁盘才是慢的,换成SSD吧。
有一件事可能会使你的脚本更快一些,那就是把你的 grep 作为 find 命令的一部分,这样就可以防止创建新的shell和进程间数据传输。
123
@stark 即使他们不从磁盘上读取,这个命令也会很慢。
You can try with xargs -P<number of logcal cores>, but, as said, the performance boost won't be great as it's a disk operation.
你使用的是什么版本的 bash
linux
bash
for-loop
while-loop
grep
user2512450
user2512450
发布于 2016-06-27
1 个回答
chepner
chepner
发布于 2016-06-27
已采纳
0 人赞同

你的代码中最大的瓶颈是,你正在运行一个单独的 mv 进程(这只是一个系统调用的包装)来重命名每个文件。假设你有100,000个文件,其中20,000个需要被重命名。你的原始代码将需要12万个进程,每个文件一个 grep ,每个重命名一个 mv 。(忽略对 find xargs 的两次调用)。

一个更好的方法是使用一种可以直接访问系统调用的语言。下面是一个简单的Perl例子。

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

这就把对mv的20,000次调用改为对perl的一次调用。

另一个瓶颈是为每个文件运行一个grep进程。相反,你希望每次都能将尽可能多的文件传递给grep。这里不需要xargs;使用-exec主程序来代替find

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