GN_
GNU Parallel是xargs和shell循环的替代品,可以利用多核CPU并行执行任务(jobs),加快命令行脚本的运行速度。这里的任务(job)可以是单个命令或者一个shell小脚本。并行执行有一个任务池,将任务池中任务分发给每个进程单独执行,这里的任务池指输入源列表。一般输入源包括文件列表、主机列表、用户列表等等,每一个列表项就是一个任务的参数。本质上,parallel工具是一个perl脚本。
安装
如果机器上没有parallel命令,可以自行安装。
1 2 | $ (wget -O - pi.dk /3 || lynx - source pi.dk /3 || curl pi.dk /3/ || \ fetch -o - http: //pi .dk /3 ) > install .sh $ bash install .sh |
并行执行
paralle可以替代xargs -P功能
1 2 3 4 5 6 | # sleep 1+2+3+4+5 = 15秒 $ time echo {1..5} | xargs -n 1 sleep #使用5个进程,最长的需要5秒 $ time echo {1..5} | xargs -n 1 -P 5 sleep # -n接受最多参数,-P最大进程数 # 以上命令可用paralle代替 $ time parallel sleep {} ::: {1..5} |
{}
是参数占位符,可以省略,具体参数来自于后面的:::
输入源列表。
我们看看parallel执行情况。
1 2 | $ parallel sleep {} ::: {30..40} $ ps aux | grep sleep |
从下图可以看出paralle命令根据输入的参数列表启动了多个进程运行,并且可以看出paralle命令实际上是一个perl脚本。
输入源
parallel可以从文件、命令行、stdin、管道读入参数列表。
1 2 3 | $ parallel echo ::: A B C # 命令行输入 $ parallel -a abc- file echo # 文件 $ cat abc- file | parallel echo # 管道 |
多个输入源
如果命令需要多个参数,可以使用:::
连接多组输入源,不同输入源两两组合。
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ parallel echo ::: A B C ::: D E F # 多组输入源组合 A D A E A F B D B E B F C D C E C F $ parallel -a abc- file -a def- file echo $ cat abc- file | parallel -a - -a def- file echo #使用 - 组合 $ cat abc- file | parallel echo :::: - def- file # ::::代替- |
命令可以是bash函数
1 2 3 4 5 6 | # only works in bash my_func() { echo in my_func $1 } export -f my_func parallel my_func ::: 1 2 3 |
替换字符串
预定义的替换字符串操作,可以用于路径文件名处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $ parallel echo ::: A /B .C #不改变字符串,等价于{} A /B .C $ parallel echo {} ::: A /B .C #缺省 A /B .C $ parallel echo {.} ::: A /B .C #去除扩展名 A /B $ parallel echo {/} ::: A /B .C #去除路径 B.C $ parallel echo { // } ::: A /B .C #保留路径 A $ parallel echo {/.} ::: A /B .C #去除路径和扩展 B $ parallel echo { #} ::: A B C #给出任务号,从1到任务总数 1 2 3 $ parallel echo {2} {1} ::: A B C ::: D E F # {n} 第n组输入源 $ parallel echo {2} ::: A B C ::: D E F |
1 | $ seq 5 | parallel echo placeholder_{} |
控制并行任务数
默认情况下,paralle命令会自动侦测到节点可用CPU核数,并且尽可能多的使用CPU核并发运行作业。
比如测试的节点有32个CPU核,
1 2 3 4 5 6 7 8 | $ parallel echo {%} ::: {1..33} 1 2 3 ... 31 32 1 |
使用{%}
输出job slot number,不同于任务号,根据同时并发运行任务数,将所有任务分配到每一个slot槽中并分配一个序号。如果任务总数超过允许的同时并发数目,一个slot中会分配超过一个任务数。上面例子中由于使用节点只有32个核,最多并发运行32个作业,因此slot number在1-32之间。
使用--jobs
或者-j
控制允许同时运行的任务数。
1 2 3 4 5 6 7 8 9 10 11 | $ parallel --jobs 4 echo {%} ::: {1..33} 1 2 3 4 1 2 3 4 ... $ parallel --jobs 2 "sleep {}; echo {}" ::: 5 4 3 2 1 |
查看执行命令但不实际执行
使用--dryrun
查看执行命令,但不实际执行,常用于开发验证阶段。
1 2 3 4 5 6 7 8 9 10 | $ parallel --dryrun echo {2} {1} ::: bird flower fish ::: Red Green Blue echo Red bird echo Green bird echo Blue bird echo Red flower echo Green flower echo Blue flower echo Red fish echo Green fish echo Blue fish |
{2} {1}
指定输入源顺序,第二组输入在前。
控制输出顺序
--keep-order
或者-k
控制输出顺序与输入顺序相同。默认是乱序的,按照命令执行快慢。
1 | $ parallel --keep-order --tag "sleep {}; echo {}" ::: 5 4 3 2 1 |
管道模式
1 2 3 4 | # 大文件切割多个块并行处理 $ cat bigfile | parallel --pipe wc -l $ parallel --pipepart -a bigfile --block -l --recstart ">" grep foobar |
循环并行
parallel命令代替shell循环for/while。下面示例中括号()
中执行for或者while循环,是将命令放在子shell中执行。加括号执行好处是命令组合更加清晰,同时限定变量的作用范围。
1 2 3 4 5 6 7 8 9 10 11 12 | # Standard for-loop that does something with each line in a file called 'list' ( for x in ` cat list` ; do do_something $x done ) | process_output # Standard while-read-loop that does something with each line in a file called 'list' cat list | ( while read x ; do do_something $x done ) | process_output # Converted into GNU parallel command cat list | parallel do_something | process_output |
实例
wgrib2裁剪小区域grib数据
- 生成文件列表再通过管道并行执行
1 2 3 4 5 6 7 | #!/bin/bash #生成文件列表 for file in `ls 2022072600/*.grb2` do echo $file >> fnames.txt done # 使用4个进程并行执行 cat fnames.txt | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2 # {/.}去除路径和后缀名 |
- 可以不用临时文件fnames.txt
1 | $ ls 2022072600/*grb2 | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2 |
或者
1 | $ find 2022072600 -name "*.grb2" - type f | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2 |
参考资料


MetMan's Blog
https://blog.metman.top/index.php/archives/102/(转载时请注明本文出处及文章链接)
《署名-非商业性使用-相同方式共享 4.0 国际 》许可协议授权