既生xargs何生parallel
目录
xargs和parallel都能并发运行多个命令,然而对这两个命令的区别一直不太清楚,直到看到了这篇文章:https://www.gnu.org/software/parallel/parallel_alternatives.html#DIFFERENCES-BETWEEN-xargs-AND-GNU-Parallel
简单的说,parallel就是增强版的xargs。
xargs 不能很好的处理特殊字符(比如空格, \, ' 和 ")
比如下面这个例子
touch important_file
touch 'not important_file'
ls not* | xargs rm
会删除 import_file
,并提示 rm: 无法删除 'not': 没有那个文件或目录
mkdir -p "My brother's 12\" records"
ls | xargs rmdir
会提示 /usr/bin/xargs: 未匹配的 单 引用;默认情况下,引用是针对 xargs 的,除非您使用了 -0 选项
和 rmdir: 删除 'My' 失败: 没有那个文件或目录
touch 'c:\windows\system32\clfs.sys' echo 'c:\windows\system32\clfs.sys' | xargs ls -l
会提示: ls: 无法访问 'c:windowssystem32clfs.sys': 没有那个文件或目录
一般遇到这种情况你需要使用xargs的 -0
参数来指定使用 NUL
作为分隔符,然而这同时要求产生输入的命令的支持。
还有一种变通方案是使用 xargs 的 -d
选项指定使用换行符作为分隔符,比如:
touch 'c:\windows\system32\clfs.sys' touch 'not important_file' ls |xargs -d "\n" -n1 rm
xargs 没有参数直接按CPU Core数量来并法运行任务,而必须人工指定并法度
在parallel默认依据CPU Core数量来决定并发的任务数,而且除了能通过 -P N
直接设定并发度外,还能通过 -P +N
, -P -N
, -P N%
来在CPU Core的数量基准进行调整(减少N个,增加N个,乘于N%)
而xargs只能通过 -P N
来手工设定并法度, 不过这也不是什么太大的问题,我们可以通过 grep processor /proc/cpuinfo |wc -l
来统计CPU的Core数量
xargs 可能会导致输出串行
也就是可能一行输出的前半部分是一个进程的输出,而后半部分是另一个进程的输出。例如:
slow_seq() { echo Count to "$@" seq "$@" | perl -ne '$|=1; for(split//){ print; select($a,$a,$a,0.100);}' } export -f slow_seq echo "这是期望值" seq 8 | xargs -n1 -P1 -I {} bash -c 'slow_seq {}' echo "使用parallel进行并发" seq 8 | parallel -P8 slow_seq {} echo "使用xargs进行并发" seq 8 | xargs -n1 -P8 -I {} bash -c 'slow_seq {}'
这是期望值 Count to 1 1 Count to 2 1 2 Count to 3 1 2 3 Count to 4 1 2 3 4 Count to 5 1 2 3 4 5 Count to 6 1 2 3 4 5 6 Count to 7 1 2 3 4 5 6 7 Count to 8 1 2 3 4 5 6 7 8 使用parallel进行并发 Count to 1 1 Count to 2 1 2 Count to 3 1 2 3 Count to 4 1 2 3 4 Count to 5 1 2 3 4 5 Count to 6 1 2 3 4 5 6 Count to 7 1 2 3 4 5 6 7 Count to 8 1 2 3 4 5 6 7 8 使用xargs进行并发 Count to 1 Count to 2 Count to 3 Count to 4 11Count to 5 11Count to 6 Count to 7 1Count to 8 111 2222222 333333 44444 5555 666 77 8
xargs 不支持保持多任务输出的顺序
parallel 可以通过 -k
或 --keep-order
选项来保证多任务的输出顺序与顺序执行时的顺序一直。
而xargs无此功能,也就是说任务有多行输出的话,可能导致多个任务的输出混杂在一起.
xargs 不支持远程运行任务
parallel可以通过 --sshlogin
来将任务发送到远程主机上并发运行.
其他与远程运行的命令包括 --ssh
, --return
, --transfer
, --transferfile
, --tf
, --cleanup
, --env
这个能力极其恐怖,下面是摘自 man parallel
中的一个远程执行任务的例子:
EXAMPLE: Using remote computers To run commands on a remote computer SSH needs to be set up and you must be able to login without entering a password (The commands ssh-copy-id, ssh-agent, and sshpass may help you do that). If you need to login to a whole cluster, you typically do not want to accept the host key for every host. You want to accept them the first time and be warned if they are ever changed. To do that: # Add the servers to the sshloginfile (echo servera; echo serverb) > .parallel/my_cluster # Make sure .ssh/config exist touch .ssh/config cp .ssh/config .ssh/config.backup # Disable StrictHostKeyChecking temporarily (echo 'Host *'; echo StrictHostKeyChecking no) >> .ssh/config parallel --slf my_cluster --nonall true # Remove the disabling of StrictHostKeyChecking mv .ssh/config.backup .ssh/config The servers in .parallel/my_cluster are now added in .ssh/known_hosts. To run echo on server.example.com: seq 10 | parallel --sshlogin server.example.com echo To run commands on more than one remote computer run: seq 10 | parallel --sshlogin s1.example.com,s2.example.net echo Or: seq 10 | parallel --sshlogin server.example.com \ --sshlogin server2.example.net echo If the login username is foo on server2.example.net use: seq 10 | parallel --sshlogin server.example.com \ --sshlogin foo@server2.example.net echo If your list of hosts is server1-88.example.net with login foo: seq 10 | parallel -Sfoo@server{1..88}.example.net echo To distribute the commands to a list of computers, make a file mycomputers with all the computers: server.example.com foo@server2.example.com server3.example.com Then run: seq 10 | parallel --sshloginfile mycomputers echo To include the local computer add the special sshlogin ':' to the list: server.example.com foo@server2.example.com server3.example.com : GNU parallel will try to determine the number of CPUs on each of the remote computers, and run one job per CPU - even if the remote computers do not have the same number of CPUs. If the number of CPUs on the remote computers is not identified correctly the number of CPUs can be added in front. Here the computer has 8 CPUs. seq 10 | parallel --sshlogin 8/server.example.com echo
xargs 不支持context替换
parallel 的 -X
选项可以让 {}
替换成命令行参数允许的最多参数值,若并发运行多个任务,则这些参数会被平均地分给每个任务,这种多参数的替换被称为context替换。例如
seq 1 123|parallel -X -I{} echo {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
xargs使用 -I 选项时,只能替代一个参数.
xargs的 -I
选项默认了 -L 1
,也就是说每个命令行只能使用最多一行输入作为参数。
而parallel中没有这个限制
seq 1 11 |parallel -I{} -l 2 echo
1 2 3 4 5 6 7 8 9 10 11
当命令中包含复合命令,管道或IO重定向时,xargs需要将之包装在 bash -c中
在xargs中,若将命令用括号引用起来,则xargs将整个括号的内容当成是一个命令名,也就是类似于parallel中的 -q
选项:
所以当你执行
ls | xargs -d "\n" -I {} "wc {} >{}.wc"
时,xargs会尝试查找一个叫做 wc XXX>XXX.wc
的命令,结果就是提示找不到该命令。
正确的做法是改成
ls | xargs -d "\n" -P8 -I {} bash -c "wc {} >{}.wc"
而使用parallel就没那么复杂了,直接用引号引起来就行了:
ls | parallel "wc {} >{}.wc"