暗无天日

=============>DarkSun的个人博客

既生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"