并行外壳循环


11

我想处理许多文件,并且由于这里有很多核心,所以我想并行执行:

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邮件列表的讨论现在已经很老了。那么你知道更好吗?


与此问题类似:superuser.com/questions/153630/…看看该技术是否对您有用
JRobert

如果您发布了错误消息,将很有帮助。
暂停,直到另行通知。

@JRobert是的,我知道这一点,但这实际上并没有帮助,因为makefile方法无法像我所说的那样工作!@Dennis:好的,首先让我展示一个超过指定数量的进程。其次,它无法正确返回提示。第三,我说会使作业撤消是不对的:我只是echo "DONE"在循环之后放置了一个指示器,该指示器在活动作业尚未完成之前就已执行。=>这使我认为工作没有完成。
数学

Answers:


15

生成文件解决您的问题的好方法。您可以在shell中编写此并行执行的程序,但是您已经注意到,这很困难。并行执行make不仅要处理启动作业并检测其终止,还要处理负载平衡,这很棘手。

遍历的需求不是障碍:有支持它的make实现。GNU make,具有通配符扩展如$(wildcard *.c)和shell访问之类$(shell mycommand)(有关更多信息,请参见GNU make手册中的查找功能)。这是makeLinux 上的默认设置,并且在大多数其他系统上可用。这是一个Makefile框架,您可以根据需要进行调整:

来源= $(通配符* .src)

全部:$(sources:.src = .tgt)

%.tgt:$。src
    do_something $ <$$(derived_pa​​rams $ <)> $ @

运行类似的命令make -j4以并行执行四个作业,或make -j -l3将平均负载保持在3附近。


8

我不确定您的派生参数是什么样的。但是,使用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

http://www.youtube.com/watch?v=OpaiGYxkSuQ上观看GNU Parallel的介绍视频


7

使用shell的wait命令对您有用吗?

for i in *
do
    do_something $i &
done
wait

您的循环执行一个作业,然后等待它,然后执行下一个作业。如果上述方法对您不起作用,那么如果您移动pwait,可能会更好done


没有100万个文件,我将运行100万个进程,还是我错了?
数学

1
@brubelsabs:好吧,它将尝试执行一百万个过程。您没有在问题中说需要处理多少个文件。我认为您需要使用嵌套for循环来限制该限制:(for file in *; do for i in {1..10}; do do_something "$i" & done; wait; done未嵌套)一次应执行十次,并等到每个组的所有十个都完成后再开始下一个十个。您的循环一次完成一个&。有关其他选择,请参见JRobert链接到的问题。在堆栈溢出中搜索与您(和那个)类似的其他问题。
暂停,直到另行通知。

如果OP预期有一百万个文件,那么他将对for i in *。他将不得不使用管道或其他东西将参数传递给循环。然后,您可以运行一个递增计数器并运行"micro-"wait"-s"每个“ $((i%32))”

@DennisWilliamson:结合wait内部计数器循环对我来说效果很好。谢谢!
2014年

3

为什么还没有人提到xargs?

假设您正好有三个参数,

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

否则请使用定界符(为此方便使用null):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

编辑:对于上述情况,每个参数都应由一个空字符分隔,然后应使用xargs -n指定参数的数量。


是的,在我们的项目中,有人也有相同的想法,即使在带有MSys的Windows下,它也能很好地工作。
数学

0

我尝试了一些答案。它们使脚本比所需的要复杂一些。理想情况下使用parallelxargs将是更可取的,但是如果for循环内的操作很复杂,则创建大而长的行文件以并行提供可能会遇到问题。相反,我们可以使用source如下

# 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迭代的功能独立性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.