GNU Parallel是一个Linux下的工具,为了在一台或多台计算机上并行的执行计算任务,一个计算任务可以是一条shell命令或者一个以每一行做为输入的脚本程序。通常的输入是文件列表、主机列表、用户列表、URL列表或者表格列表;一个计算任务也可以是一个从管道读取的一条命令。GNU Parallel会把输入分块,然后通过管道并行的执行。

如果你会使用xargs和tee命令,你会发现GNU Parallel非常易于使用,因为GNU Parallel具有与xargs一样的选项。GNU Parallel可以替代大部分的shell循环,并且用并行的方式更快的完成计算任务。

GNU Parallel保证它的输出与顺序执行计算任务时是一样的,这样就可以方便的把GNU Parallel的输出做为其它程序的输入。

对于每一行输入,GNU Parallel会把这一行做为参数来运行指定的命令。如果没有给出命令,那么这一行会被当做命令执行。多行输入会并行的运行。GNU Parallel经常被用于替代xargs或者cat | bash。

                                                                                                                                                                                                                                                         ---引用自某网络文章                      

2.1 入门小例子

下面是一个小例子,帮助你了解Parallel的威力:

假设你已经知道seq命令的用途,在linux执行seq 5,会得到如下结果:

root@i-s0tsk03r:~# seq 5

1

2

3

4

5

试下执行seq 5 | parallel seq {} '>' example.{},这条命令相当于:

seq 1 > example.1

seq 2 > example.2

seq 3 > example.3

seq 4 > example.4

seq 5 > example.5

seq 5生成了一个列表,包含“1,2,3,4,5”这五个元素,通过管道符"|"传递给Parrallel,Parrallel中的‘{}’类似于占位符,所以就变成了上述代码块中的五行代码,这里有个小技巧,使用--dry-run选项可以打印出来Parrallel实际执行的命令:seq 5 | parallel --dry-run seq {} '>' example.{}

2.2 输入源

GNU Parallel从输入源中读取数据,每个输入源都是一个命令行,输入源跟着‘:::’这个符号后面:

parallel echo ::: 1 2 3 4 5

命令结果:(顺序可能和实际有所不同)

1

2

3

4

5

如果程序的输入源是一些文件的时候,那么这个时候使用Parallel将非常便捷,例如:

parallel wc ::: example.*

根据上面入门小例子生成的五个文件(example.1,example.2,example.3,example.4,example.5),使用--dry-run选项,实际执行的应该是如下命令:

wc example.1

wc example.2

wc example.3

wc example.4

wc example.5

      程序运行结果是

1 1 2 example.1

2 2 4 example.2

3 3 6 example.3

4 4 8 example.4

5  5 10 example.5

      如果你使用了多个:::,GNU Parallel将生成所有输入源的组合

parallel echo ::: S M L ::: Green Red

      同样的,我们使用--dry-run命令看看实际执行的是什么

echo S Green

echo S Red

echo M Green

echo M Red

echo L Green

echo L Red

结果当然会输出六行,相当于输入源3*2=6

Parallel也支持从标准输入读入,类似最开始seq的例子,下面是一个新的例子:

find example.* | parallel echo File

我们使用--dry-run命令看看实际执行的是什么

echo File example.1

echo File example.2

echo File example.3

echo File example.4

echo File example.5

        这个命令其实相当于:

parallel echo File ::: example.*

2.3 构建命令行

        Shell命令是在:::之前的,我们可以为为命令行添加命令行选项:

parallel wc -l :: example.*

在这里我们为wc指定了选项-l,输出结果为(顺序可能和实际有所不同):

1 example.1

2 example.2

3 example.3

4 example.4

5 example.5

上述的命令里面可以包含多条Shell命令(或者多个程序),只要保证每条命令之间用";"分割开来即可(shell的语法,多个命令或者程序写在一行需要用“;”分割)

执行:parallel --dry-run echo counting lines';' wc -l ::: example.*

echo counting lines; wc -l example.1

echo counting lines; wc -l example.2

echo counting lines; wc -l example.3

echo counting lines; wc -l example.4

echo counting lines; wc -l example.5

输出结果为(顺序可能和实际有所不同):

counting lines

1 example.1

counting lines

2 example.2

counting lines

3 example.3

counting lines

4 example.4

counting lines

5 example.5

输入源的值通常附加到命令后面,通过使用{},我们可以在任意地方把输入源的值替换到{}:

parallel  --dry-run echo counting {}';' wc -l {} ::: example.*

实际的命令如下:

echo counting example.1; wc -l example.1

echo counting example.2; wc -l example.2

echo counting example.3; wc -l example.3

echo counting example.4; wc -l example.4

echo counting example.5; wc -l example.5

      {}替换了example.*中的每个值,实际上{}只适用于只有一个:::的情况,如果有多个:::,我们可以分别使用{1},{2}...{n}来替代每个:::后的输入源,下面是一个例子:

parallel --dry-run echo count {1} in {2}';' wc {1} {2} ::: -l -c ::: example.*

#这个例子比较复杂,我们需要用dry-run看看实际到底执行了什么,实际执行的命令如下:

echo count -l in example.1; wc -l example.1

echo count -l in example.1; wc -l example.1

echo count -l in example.2; wc -l example.2

echo count -l in example.3; wc -l example.3

echo count -l in example.4; wc -l example.4

echo count -l in example.5; wc -l example.5

echo count -c in example.1; wc -c example.1

echo count -c in example.2; wc -c example.2

echo count -c in example.3; wc -c example.3

echo count -c in example.4; wc -c example.4

echo count -c in example.5; wc -c example.5

 

先看下输入源:

第一个输入源:::对应的是-l -c

第二个输入源:::对应的是example.*

      所以{1}代表的是{-l  -c}这个集合,{2}代表的是{example.1 example.2 example.3 example.4 example.5}这个集合,然后根据组合2*5=10,一共生成了如上的十个命令。

      看到这里,如果你考虑用parallel去改造你之前的shell中的循环,那应该是一个相当棒的主意!

2.4 输出控制

      Parallel的输出会随着所有命令结束而被立即打印出来,这就意味着输出的顺序可能和输入的顺序不完全相同,例如:

parallel sleep {}';' echo {} done ::: 5 4 3 2 1

#这个命令一眼看上去结果应该是

5 done

4 done

3 done

2 done

1 done

#但是实际可能是

1 done

2 done

3 done

4 done

5 done

原因是什么呢,可能是因为sleep的时候,多进程切换的顺序是不固定的,如果我们想要强制有序输出,那么可以指定参数--keep-order/-k,这样得到的结果就会是有序的,例

parallel -k sleep {}';' echo {} done ::: 5 4 3 2 1

输出结果为(结果顺序一定是预期的):

5 done

4 done

3 done

2 done

1 done

2.5 执行控制

如果你的任务是计算密集型的,Parallel将帮助你在每个CPU上运行一个任务,达到并行的效果。

但是有时候你希望能够运行更多的任务,你可以通过-j/--jobs选项控制任务槽(执行任务的单元)。为parallel里面指定--jobs参数,这里我们可以指定parallel运行槽为2:

parallel --jobs 2 sleep {}';' echo "jobs:" {%} 'echo' {} done ::: 5 4 3 1 2

输出结果为(顺序可能和实际有所不同):

jobs: 2 echo 4 done

jobs: 1 echo 5 done

jobs: 1 echo 1 done

jobs: 2 echo 3 done

jobs: 1 echo 2 done

两个job的槽会花1~5分钟来完成这五个任务的处理。在这里,我们使用了{%}来打印job的id(类似于进程id)。可以看到五个输入被分为如下的序列:

Job slot 1:5 1 2

Job slot 2:4 3

当然了,你可以通过指定'--job 5'让五个任务并行起来,这样所有的五个任务会同时启动,但是它们会在不同时刻结束。

parallel --jobs 5 sleep {}';' echo "jobs:" {%} 'echo' {} done ::: 5 4 3 1 2

输出

jobs: 4 echo 1 done

jobs: 5 echo 2 done

jobs: 3 echo 3 done

jobs: 2 echo 4 done

jobs: 1 echo 5 done

所有的任务都是并行运行的:

Job slot 1:5

Job slot 2:4

Job slot 3:3

Job slot 4:1

Job slot 5:2

你可以传递'--job 0'来尽量让任务跑满所有CPU,而不是手动指定job的number,这样效率会更高。

2.6 管道模式

Parallel可以通过标准输入传递数据块给命令行:

seq 1000000 | parallel --pipe wc

#这里比较疑惑地方是加--pipe和不加该选项有什么区别呢?

#seq 1000000 | wc,相当于生成了一个数据块,通过标准输入给了wc,所以结果应该是

1000000 1000000 6888896

#wc命令会打印出行数,字符数,字节数,拿seq 11举例子,输出应该是

1

2

3

4

5

6

7

8

9

10

11

#那么,seq 11 | wc ,应该是11,11,24,第一个11代表有11行,第二个11代表有11个字符,第三个24相当于(1到9,10拆分成1和0,11拆分成1和1,然后一共附加11个换行符)

#如果不加--pipe呢,seq 100000 | parallel  wc会报错,此时找不到wc 1到wc 100000这些命令

seq 1000000 | parallel --pipe wc的输出(顺序可能和实际有所不同):

165668  165668 1048571

149796  149796 1048572

149796  149796 1048572

149796  149796 1048572

149796  149796 1048572

  85352  85352  597465

149796  149796 1048572

这样相当于parallel内部对数据块进行了切开,然后并行多个wc去处理一小块数据,没有写代码就轻松实现了并行的大数据文件处理,是不是很神奇!

Tips:GNU Parallel会对大数据块以'\n'进行拆分(即加一个换行符),拆分后的每个部分大小不会超过1MB,所以对于大数据块的程序处理机器有帮助。

2.7 小结

相信你已经掌握了GNU Parallel的基本使用方法,在大部分的场景下,可能已经够用了。

剩下的部分会讨论更多Parallel的使用细节,覆盖更多的使用场景。

个人声明:本文翻译源于GNU Parallel 2018官方文档,很多地方加入了自己的理解,由于翻译水平有限,可能存有勘误。如果有涉及到版权问题,请直接邮件与我联系:[email protected]

博主:测试生财

座右铭:用测试完成原始积累,用投资奔向财务自由

csdn:https://blog.csdn.net/ccgshigao

博客园:https://www.cnblogs.com/qa-freeroad/

51cto:https://blog.51cto.com/14900374