安装

brew install parallel

官方文档指南

man parallel_tutorial

输入源

直接在命令行传递参数

::: 参数1 ::: 参数2
$ echo ::: a b c e
b
a
c
e
$ parallel echo ::: a b c e ::: f g j i
a g
a j
a i
b f
b j
b i
b g
c f
c g
c j
c i
e f
e g
a f
e j
e i

以文件作为输入

-a file1 -a file2

或 

:::: file1 :::: file2
parallel -a abc-file echo

parallel -a a.file -a b.file echo

这样子就可以并行输出文件 abc-file 的内容(默认情况下, 是无序的, 如果想有序, 则传参数 -k 即可)

以标准输输入作为输入 stdin

-

直接 - 后不接其他东西即可.

例如

$ seq 5 | parallel  echo -
2
4
3
1
5

混合

{% raw %}:::{% endraw %} 可以和 {% raw %}::::{% endraw %} 混合使用.

跳过空行

parallel --no-run-if-empty

指定输入结束符

$ parallel -E stop echo ::: A B stop C D
A
B

修改参数界定符

使用 NUL 作为办分割符

--null
或
-0

修改文件界定符

默认情况下 {% raw %}::::{% endraw %} 作为文件的分界符, 可以使用

--arg-file-sep sep-str

来修改

修改参数界定符

默认情况下, {% raw %}:::{% endraw %} 作为参数分界符, 可以使用

--arg-sep sep-str

来修改

显示进度

--bar

或
--eta

或
--progress

输出到文件

--joblog
parallel --joblog /tmp/log
seq 1000 | parallel -j30 --bar '(echo {};sleep 0.1)'

使用指定分割符

-d delim

指定输入项是以 delim 来结束的.

替换符

{% raw %}{}{% endraw %}

默认情况下, {% raw %}{}{% endraw %} 代表输入项.

可以通过 -I 来修改.

{% raw %}{.}{% endraw %}

将输入项去掉扩展名后输出.

例如

$ parallel --no-run-if-empty --bar -a test.file echo {} {.}
0% 0:4=0s sub.dir/bar 

sub.dir/bar sub.dir/bar
25% 1:3=0s sub.dir/bar

sub.dir/foo.jpg sub.dir/foo
50% 2:2=0s sub.dir/bar

subdir/foo.jpg subdir/foo
75% 3:1=0s sub.dir/bar

foo.jpg foo
100% 4:0=0s sub.dir/bar

{% raw %}{/}{% endraw %}

获取输入项的文件名

{% raw %}{/.}{% endraw %}

获取输入项的文件名, 但去掉扩展名

{% raw %}{//}{% endraw %}

获取输入项的目录名

`{% raw %}

{% endraw %}`

任务的ID

{% raw %}{%}{% endraw %}

任务的 slot 数字

$ parallel --no-run-if-empty  -a test.file echo {#} {%} {.}
2 2 subdir/foo
1 1 foo
3 3 sub.dir/foo
4 4 sub.dir/bar

{% raw %}{n}{% endraw %}

输入源的第 n 个参数.

$ parallel  echo  {2} ::: a b c ::: "这是第二个参数" ::: "这是第三个参数"
这是第二个参数
这是第二个参数
这是第二个参数

{% raw %}{n.}{% endraw %}

{% raw %}{n}{% endraw %} , 只是去掉了文件扩展名

{% raw %}{n/}{% endraw %}

{% raw %}{/}{% endraw %} , 只是处理第 n 个参数的情况

{% raw %}{n//}{% endraw %}

{% raw %}{//}{% endraw %}, 只是处理第 n 个参数的情况

{% raw %}{n/.}{% endraw %}

{% raw %}{/}{% endraw %} , 只是处理第 n 个参数的情况, 并且去掉扩展名.

限制参数长度和大小

--xargs

可以让 parallel 支持多个参数

有无 xargs 的区别

$ parallel --xargs echo  ::: a b c ::: "这是第二个参数" ::: "这是第三个参数"
a 这是第二个参数 这是第三个参数 b 这是第二个参数 这是第三个参数 c 这是第二个参数 这是第三个参数
$ parallel echo  ::: a b c ::: "这是第二个参数" ::: "这是第三个参数"
a 这是第二个参数 这是第三个参数
b 这是第二个参数 这是第三个参数
c 这是第二个参数 这是第三个参数

--jobs 4

指定任务数

-N3

限制参数最大个数

处理空格

--trim <n|l|r|lr|rl>  Trim white space in input.

输出打印处理

前缀

--tag 

以参数作为输出前缀

$ seq 10 | parallel --tagstring "hello {} ===> " echo -
hello 2 ===>    2
hello 4 ===>    4
hello 5 ===>    5
hello 6 ===>    6
hello 8 ===>    8
hello 9 ===>    9
hello 7 ===>    7
hello 3 ===>    3
hello 1 ===>    1
hello 10 ===>   10

把输出结果保存到文件

parallel --files

默认保存在 /tmp 目录中.

$ seq 10 | parallel --files --tagstring "hello {} ===> " echo -
hello 2 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parZQYda.par
hello 4 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/pariqW5T.par
hello 3 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parhkbu1.par
hello 5 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/par4ts6Z.par
hello 6 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parK3w5V.par
hello 7 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/par6UDLa.par
hello 8 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/par_twoW.par
hello 9 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parPlgWZ.par
hello 1 ===>    /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parjwUR0.par
hello 10 ===>   /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parZoyUn.par
$ cat /var/folders/lz/mpwxkp6s7rq62r08vt2h_j480000gn/T/parZQYda.par
2

详细保存结果到文件

$ seq 10 | parallel --results outdir --tagstring "hello {} ===> " echo -
hello 2 ===>    2
hello 3 ===>    3
hello 4 ===>    4
hello 5 ===>    5
hello 6 ===>    6
hello 7 ===>    7
hello 8 ===>    8
hello 9 ===>    9
hello 1 ===>    1
hello 10 ===>   10
$ tree outdir
outdir
└── 1
    ├── 1
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 10
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 2
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 3
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 4
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 5
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 6
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 7
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    ├── 8
    │   ├── seq
    │   ├── stderr
    │   └── stdout
    └── 9
        ├── seq
        ├── stderr
        └── stdout

11 directories, 30 files

控制并发数

--jobs N

或
-j N

或
--max-procs N

或
-P N

如果是0, 则意味着尽可能多的并发.(每个任务在每个CPU上尽可能 100% 占用CPU)

只打印输出命令, 则不执行

parallel --dryrun

交互式执行

parallel --interactive

恢复任务

$ parallel --joblog /tmp/log exit  ::: 1 2 3 0
$ cat /tmp/log
Seq Host    Starttime   JobRuntime  Send    Receive Exitval Signal  Command
2   :   1514099245.436       0.013  0   0   2   0   exit 2
1   :   1514099245.432       0.019  0   0   1   0   exit 1
3   :   1514099245.442       0.012  0   0   3   0   exit 3
4   :   1514099245.448       0.011  0   0   0   0   exit 0
$ parallel --resume --joblog /tmp/log exit  ::: 1 2 3 0
$ cat /tmp/log
Seq Host   Starttime    JobRuntime  Send    Receive Exitval Signal  Command
2   :   1514099245.436       0.013  0   0   2   0   exit 2
1   :   1514099245.432       0.019  0   0   1   0   exit 1
3   :   1514099245.442       0.012  0   0   3   0   exit 3
4   :   1514099245.448       0.011  0   0   0   0   exit 0

可以看到, 如果有执行过的话, --resum 则不会重新执行了. 只会执行其他还没有完成的

只重新执行失败的任务

parallel --resume-failed --joblog /tmp/log exit  ::: 1 2 3 0 0 0

限制资源占用

parallel --load 100%

parallel --noswap

parallel --memfree 1G
这表示只有内存空闲大于1G时才执行任务

parallel --nice 17

使用远程主机来执行

如果远程主机使用的是非22端口, 则中以在 ~/.ssh/config 中配置:

Host host1.v
    Port 22001
Host host2.v
    Port 22002
Host host3.v
    Port 22003

注意, 也要在远程主机上安装 parallel 才行.

parallel -S userName@host1.v -S userName@host2.v echo ::: hello

例子: 将当前目录下的文件传输到远程host然后压缩(虽然无实际意义, 这只是演示用法..)

ls | parallel -S userName@host1.v --transfer gzip {}

设置工作目录

ls | parallel --sshlogin userName@host1.v --workdir /tmp/new_dir/ --transferfile {} --return {}.gz --cleanup  gzip {}

这将会在远程主机 hot1.v/tmp/new_dir/ 上进行文件传输.

--cleanup 表示清理远程上的相应数据或文件. --return {}.gz 表示返回相应的数据或文件. --transferfile {} 表示要传输的数据或文件.

例子收集

命令在一个文件中

假设你将一个文件保存了所有要执行的命令. 例如 每行一条 curl xxxx 之类的命令.则可以这样子:

cat /tmp/ad-monitor-2018-01-30_11_51_36.sh | parallel --no-run-if-empty --progress --joblog /tmp/ad-monitor-progress.log --jobs 4 > /tmp/parallel-monitor.log

注意, 不能像以下这样子

cat /tmp/ad-monitor-2018-01-30_11_51_36.sh | parallel --no-run-if-empty --progress --joblog /tmp/ad-monitor-progress.log --jobs 4 {} > /tmp/parallel-monitor.log

如果在后面加 {} 的话, 则会有问题. 它会转义输出我们的命令(可以用 --dryrun 选项查看要执行的实际情况, 它只是输出要执行的命令, 而不会真正执行)

批量修改文件名后缀

将所有 bz2 后缀的文件, 变成 gz 后缀

# 这里是尝试看看要执行的命令是什么样子的
ls *.bz2 | parallel --dryrun mv {} {/.}.gz

#真正执行
ls *.bz2 | parallel mv {} {/.}.gz

后台并行执行命令列表

nohup parallel --no-run-if-empty --progress --joblog /tmp/ad-monitor-progress.log --jobs 2 :::: /tmp/test-parallel.sh > /tmp/parallel-monitor.log 2>&1 &

其中 /tmp/test-parallel.sh 的内容格式如下

sleep 10
curl http://www.qq.com
sleep 10
ecoh "hello"

这样子, 就可以并行执行这些命令了. 并且是在后台进程中执行.