我想处理许多文件,而且由于我这里有一堆核心,所以我想并行执行:
for i in *.myfiles; do do_something $i `derived_params $i` other_params; done
我知道 Makefile解决方案但是我的命令需要 shell 通配列表中的参数。我发现:
> function pwait() {
> while [ $(jobs -p | wc -l) -ge $1 ]; do
> sleep 1
> done
> }
>
要使用它,只需在作业和 pwait 调用之后放置 &,参数给出并行进程的数量:
> for i in *; do
> do_something $i &
> pwait 10
> done
但这种方法效果不太好,例如我尝试用 for 循环转换许多文件但出现错误并且未完成任何工作。
我不敢相信这还没完成,因为 zsh 邮件列表上的讨论已经很久了。那么你知道更好的方法吗?
答案1
一个 makefile是很好地解决了您的问题。您可以在 shell 中编写此并行执行程序,但正如您所注意到的,这很难。并行实现 make 不仅会负责启动作业并检测其终止,还会处理负载平衡,这很棘手。
通配符的要求并不是一个障碍:有支持它的 make 实现。GNU make 具有通配符扩展,例如$(wildcard *.c)
和 shell 访问,例如$(shell mycommand)
(有关更多信息,请参阅 GNU make 手册中的函数)。它是make
Linux 上的默认设置,在大多数其他系统上都可用。这是一个 Makefile 框架,您可以根据需要进行调整:
来源 = $(通配符 *.src) 全部:$(来源:.src=.tgt) %.tgt: %.src do_something $< $$(derived_params $<) >$@
运行类似并行make -j4
执行四个作业的程序,或者make -j -l3
保持平均负载在 3 左右。
答案2
我不确定你的派生参数是什么样的。但使用 GNU Parallel http://www.gnu.org/software/parallel/,你可以这样做,每个 CPU 核心运行一个作业:
find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
echo "$name - $upper"'
如果您想要得到的只是改变 .extension,则 {.} 可能会很方便:
parallel -j+0 lame {} -o {.}.mp3 ::: *.wav
观看 GNU Parallel 的介绍视频http://www.youtube.com/watch?v=OpaiGYxkSuQ
答案3
使用 shell 的wait
命令对您不起作用吗?
for i in *
do
do_something $i &
done
wait
您的循环执行一项作业,然后等待它,然后执行下一项作业。如果上述方法对您不起作用,那么如果您pwait
在之后移动,您的循环可能会更好地工作done
。
答案4
我尝试了一些答案。它们使脚本比需要的更复杂一些。理想情况下,使用parallel
或xargs
会更好,但是如果 for 循环中的操作很复杂,则创建大而长的行文件以提供给并行可能会有问题。相反,我们可以使用源,如下所示
# Create a test file
$ cat test.txt
task_test 1
task_test 2
# Create a shell source file
$ cat task.sh
task_test()
{
echo $1
}
# use the source under bash -c
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2
因此,对于你的问题解决方案如下
for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done
将做某事定义为do_something.sh
do_something(){
process $1
echo $2
whatever $3
}
用xarg
或执行gnu parallel
cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'
我假设 for 的迭代函数独立性是隐含的。