使用xargs并行运行程序

125

我目前拥有当前的脚本。

#!/bin/bash
# script.sh

for i in {0..99}; do
   script-to-run.sh input/ output/ $i
done

我希望使用xargs并行运行它。我已经尝试过

script.sh | xargs -P8

但是以上操作一次只执行一次。尝试使用-n8也不行。 在要执行的脚本循环行末尾添加&会尝试一次性运行99次脚本。如何才能每次仅运行8个循环,总共达到100次。


这是我最初想做的事情,但由于我使用的是Windows系统,不得不使用xargs。我无法在Windows上运行GNU Parallel。 - Olivier
这个脚本是在调用自己吗?还是你在提问时搞混了名字? - Etan Reisner
抱歉,它应该调用另一个脚本。我会修复它。 - Olivier
这里与 https://dev59.com/yHA75IYBdhLWcg3wYYBQ 的答案相关。 - Etan Reisner
4个回答

185

来自xargs手册页:

本手册页记录了GNU版本的xargs。 xargs从标准输入读取项目,这些项目由空格(可以用双引号、单引号或反斜杠保护)或换行符分隔,并执行该命令(默认为/bin/echo)一次或多次,后跟任何初始参数,接着是从标准输入读取的项目。标准输入上的空行将被忽略。

这意味着对于您的示例,xargs正在等待和收集脚本的所有输出,然后运行echo <that output>。不是非常有用,也不是您想要的。

-n 参数表示每个运行的命令使用输入中的多少项(仅仅是关于并行性方面的内容)。

要使用xargs实现您想要的功能,您需要执行类似以下未经测试的操作:

printf %s\\n {0..99} | xargs -n 1 -P 8 script-to-run.sh input/ output/

以下是该过程的简要说明。

  • printf %s\\n {0..99} - 按行打印从 099 的数字。
  • 运行 xargs
    • 每次运行命令行最多只使用一个参数
    • 同时最多运行八个进程

10
实际上您不需要将参数放在单独的行上;xargs会对其进行单词分割。因此,echo {0..99} | 同样可以正常工作。然而, <<<{0..99} 似乎无法正常工作;尽管 <<<word 被文档描述为花括号展开单词,但这在我手头可用的任何 bash 版本中都不起作用。 - rici
1
@rici 看起来是文档错误,特别是因为 Here Documents 的文档 没有 提到花括号扩展(在快速测试中也不会发生),尽管它们也没有提到波浪线扩展(这对于 << 不会发生,但对于 <<< 会发生,所以 *耸肩*)。在这里文档和字符串中发生和不发生的扩展有点奇怪。 - Etan Reisner
1
你如何通过例如换行符将不同运行的结果分开? - nirvana-msu
8
这段命令的意思是:运行time head -12 <(yes "1") | xargs -n1 -P4 sleep,会执行12个sleep 1命令,并发地执行4个。该命令将花费3秒钟的时间。 - Walter A
3
值得注意的是,使用参数-P 0会利用系统上的CPU数量。 - slf
你也可以使用seq 0 99来获得数字序列(使用换行符分隔;至少在Linux上有效)。 - jena

74

使用 GNU Parallel,您可以执行以下操作:

parallel script-to-run.sh input/ output/ {} ::: {0..99}

如果您不想每个CPU核心运行一个作业,请添加-P8

与相反的xargs相比,即使输入包含空格、'或"(但此处不是这种情况),它也会做正确的事情。它还确保来自不同作业的输出不会混合在一起,因此,如果您使用输出,则保证不会从两个不同的作业中获得半行。

GNU Parallel是一个通用的并行程序,可以轻松地在同一台计算机或具有ssh访问权限的多台计算机上并行运行作业。

如果您有32个不同的作业要在4个CPU上运行,一种简单的并行化方法是在每个CPU上运行8个作业:

Simple scheduling

相反,GNU Parallel在一个进程完成时生成一个新进程-保持CPU活动状态,从而节省时间:

GNU Parallel scheduling

安装

如果您的发行版没有打包GNU Parallel,您可以进行个人安装,无需root访问权限。只需要执行以下操作即可在10秒钟内完成:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 883c667e01eed62f975ad28b6d50e22a
12345678 883c667e 01eed62f 975ad28b 6d50e22a
$ md5sum install.sh | grep cc21b4c943fd03e93ae1ae49e28573c0
cc21b4c9 43fd03e9 3ae1ae49 e28573c0
$ sha512sum install.sh | grep da012ec113b49a54e705f86d51e784ebced224fdf
79945d9d 250b42a4 2067bb00 99da012e c113b49a 54e705f8 6d51e784 ebced224
fdff3f52 ca588d64 e75f6033 61bd543f d631f592 2f87ceb2 ab034149 6df84a35
$ bash install.sh

更多安装选项请参见http://git.savannah.gnu.org/cgit/parallel.git/tree/README

了解更多

查看更多示例:http://www.gnu.org/software/parallel/man.html

观看介绍视频:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

按照教程操作:http://www.gnu.org/software/parallel/parallel_tutorial.html

注册邮件列表以获取支持:https://lists.gnu.org/mailman/listinfo/parallel


35
这并没有回答这个问题,也没有指出为什么xargs不能达到同样的效果。 - 张实唯
13
因为对我来说xarg的作用与第二张图片所示完全一样,所以我会给它点个踩。 - noonex
3
你知道吗,不是每个人都使用你使用的 xargs 版本,并且 -P 选项并不在所有版本的 xargs 中都存在。 - Ole Tange
41
或许并非所有人都知道,这个答案是由GNU parallel的作者提供的。 - izkeros
8
由于一款软件存在明显广告,并且在首次尝试时无法按照描述正常运行,因为一个交互提示会破坏大多数脚本,因此被点踩。 - Daniel Sorichetti
显示剩余8条评论

13

这里是一个与 find 一起并行运行命令的示例:

find -name "*.wav" -print0 | xargs -0 -t -I % -P $(nproc) flac %

-print0 会用空字节代替换行符来终止文件名,因此我们可以在 xargs 中使用 -0 来防止将文件名中的空格视为两个不同的参数。

-t 表示详细模式,使得 xargs 打印出每个要执行的命令,如果需要的话可以保留,否则请删除。

-I % 表示将命令中的 % 替换为从标准输入读取到的参数。

-P $(nproc) 表示最大并发运行 nproc 个命令实例(nproc 可以打印出可用处理器数量)。

flac % 是我们要执行的命令,之前的 -I % 表示它会变成 flac foo.wav

另请参阅:xargs(1)手册


这个答案对我非常有帮助,因为它解释了如何使用xargs而不是其他工具。 - JuanCaicedo
小注:如果您想运行多个命令,您需要使用 bash -c。例如,如果您想要删除原始文件,则可以执行 bash -c "flac \"%\" && rm \"%\"" - Peter Frost
如果您使用长选项,例如--verbose而不是-t以及--max-procs =而不是-P,那么理解起来会更容易一些。 - bfontaine

8
您可以使用这个简单的1行命令。
seq 1 500 | xargs -n 1 -P 8 script-to-run.sh input/ output/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接