GNU Parallel 学习笔记
Contents
安装
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"
这样子, 就可以并行执行这些命令了. 并且是在后台进程中执行.