Linux xargs 命令

Linux 命令大全

xargs(英文全拼: eXtended ARGuments)是给命令传递参数的一个过滤器,也是组合多个命令的一个工具。

xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据。

xargs 也可以将单行或多行文本输入转换为其他格式,例如多行变单行,单行变多行。

xargs 默认的命令是 echo,这意味着通过管道传递给 xargs 的输入将会包含换行和空白,不过通过 xargs 的处理,换行和空白将被空格取代。

xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令。

Linux之所以需要用这个命令,关键是由于很多命令不支持|管道来传递参数,所以就有了 xargs 命令

xargs 一般是和管道一起使用。

命令格式:

somecommand |xargs -item  command

参数:

xargs是 Unix 系统的一个很有用的命令,但是常常被忽视,很多人不了解它的用法。

本文介绍如何使用这个命令。

一、标准输入与管道命令

Linux 命令都带有参数,有些命令可以接受"标准输入"(stdin)作为参数。

[root@xinbiancheng.cn]$ cat /etc/passwd | grep root
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

上面的代码使用了管道命令(|)。管道命令的作用,是将左侧命令(cat /etc/passwd)的标准输出转换为标准输入,提供给右侧命令(grep root)作为参数。

因为grep命令可以接受标准输入作为参数,所以上面的代码等同于下面的代码。

[root@xinbiancheng.cn]$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

但是,大多数命令都不接受标准输入作为参数,只能直接在命令行输入参数,这导致无法用管道命令传递参数。举例来说,echo命令就不接受管道传参。

[root@xinbiancheng.cn]$ echo "hello xinbiancheng.cn" | echo

上面的代码不会有输出。因为管道右侧的echo不接受管道传来的标准输入作为参数。

二、xargs 命令的作用

xargs命令的作用,是将标准输入转为命令行参数。

[root@xinbiancheng.cn]$ echo "hello xinbiancheng.cn" | xargs echo
hello xinbiancheng.cn

上面的代码将管道左侧的标准输入,转为命令行参数hello xinbiancheng.cn,传给第二个echo命令。

xargs命令的格式如下。

$ xargs [-options] [command]

真正执行的命令,紧跟在xargs后面,接受xargs传来的参数。

xargs的作用在于,大多数命令(比如rmmkdirls)与管道一起使用时,都需要xargs将标准输入转为命令行参数。

[root@xinbiancheng.cn]$ ls
[root@xinbiancheng.cn]$ echo "a b c" | xargs mkdir
[root@xinbiancheng.cn]$ ls
a  b  c

上面的代码等同于mkdir a b c。如果不加xargs就会报错,提示mkdir缺少操作参数,第一次通ls查看当前目录没有子目录,然后通 echo "a b c" | xargs mkdir,就创建了3个子目录,通过ls再次查看分别是a b c 目录已经创建好了。

三、xargs 的单独使用

xargs后面的命令默认是echo

$ xargs
# 等同于
$ xargs echo

大多数时候,xargs命令都是跟管道一起使用的。但是,它也可以单独使用。

输入xargs按下回车以后,命令行就会等待用户输入,作为标准输入。你可以输入任意内容,然后按下Ctrl d,表示输入结束,这时echo命令就会把前面的输入打印出来。

[root@xinbiancheng.cn]$ xargs
hi xinbiancheng.cn #Ctrl+d 快捷键
hi xinbiancheng.cn

再看一个例子。

[root@xinbiancheng.cn]$ xargs find -name
*
.
./a
./b
./c

上面的例子输入xargs find -name以后,命令行会等待用户输入所要搜索的文件。用户输入"*",表示搜索当前目录下的所有文件及目录,然后按下Ctrl d,表示输入结束。这时就相当执行find -name *

四、-d 参数与分隔符

默认情况下,xargs将换行符和空格作为分隔符,把标准输入分解成一个个命令行参数。

[root@xinbiancheng.cn]$ echo "a b c" | xargs mkdir

上面代码中,mkdir会新建三个子目录,因为xargsa b c分解成三个命令行参数,执行mkdir a b c

-d参数可以更改分隔符。

[root@xinbiancheng.cn]$ echo -e "a\tb\tc" | xargs -d "\t" echo
a b c

上面的命令指定制表符\t作为分隔符,所以a\tb\tc就转换成了三个命令行参数。echo命令的-e参数表示解释转义字符。

五、-p 参数,-t 参数

使用xargs命令以后,由于存在转换参数过程,有时需要确认一下到底执行的是什么命令。

-p参数打印出要执行的命令,询问用户是否要执行。

[root@xinbiancheng.cn]$ echo 'awk.txt grep.txt sed.txt' | xargs -p touch
touch awk.txt grep.txt sed.txt ?...y
[root@xinbiancheng.cn]$ ls
a  awk.txt  b  grep.txt  c  sed.txt

上面的命令执行以后,会打印出最终要执行的命令,让用户确认。用户输入y以后(大小写皆可),才会真正执行 touch awk.txt grep.txt sed.txt。

-t参数则是打印出最终要执行的命令,然后直接执行,不需要用户确认。

[root@xinbiancheng.cn]$ echo 'awk.txt grep.txt sed.txt' | xargs -t rm
rm awk.txt grep.txt sed.txt
[root@xinbiancheng.cn]$ ls
a  b  c

六、-0 参数与 find 命令

由于xargs默认将空格作为分隔符,所以不太适合处理文件名,因为文件名可能包含空格。

find命令有一个特别的参数-print0,指定输出的文件列表以null分隔。然后,xargs命令的-0参数表示用null当作分隔符。

[root@xinbiancheng.cn]$ find /path -type f -print0 | xargs -0 rm

上面命令删除/path路径下的所有文件。由于分隔符是null,所以处理包含空格的文件名,也不会报错。

还有一个原因,使得xargs特别适合find命令。有些命令(比如rm)一旦参数过多会报错"参数列表过长",而无法执行,改用xargs就没有这个问题,因为它对每个参数执行一次命令。

[root@xinbiancheng.cn]$ find . -name "*.txt" | xargs grep "aaa"

上面命令找出所有 TXT 文件以后,对每个文件搜索一次是否包含字符串aaa

七、-L 参数

如果标准输入包含多行,-L参数指定多少行作为一个命令行参数。

[root@xinbiancheng.cn]$ xargs find -name
"*.txt"   
"*.md"
find: paths must precede expression: `*.md'

上面命令同时将"*.txt"*.md两行作为命令行参数,传给find命令导致报错。

使用-L参数,指定每行作为一个命令行参数,就不会报错。

[root@xinbiancheng.cn]$ xargs -L 1 find -name
"*.txt"
./foo.txt
./hello.txt
"*.md"
./README.md

上面命令指定了每一行(-L 1)作为命令行参数,分别运行一次命令(find -name)。

下面是另一个例子。

[root@xinbiancheng.cn]$ echo -e "a\nb\nc" | xargs -L 1 echo
a
b
c

上面代码指定每行运行一次echo命令,所以echo命令执行了三次,输出了三行。

 xargs命令还可以从文件读取条目,而不是从标准输入读取条目。使用-a选项,后跟文件名。
通过vim创建一个ip.txt的文件,一会使用xargs命令ping里面的每一个地址:

[root@xinbiancheng.cn]$ cat ip.txt 
114.114.114.114
www.xinbiancheng.cn
202.102.128.68
使用-L 1选项,该选项表示xargs一次读取一行。如果省略此选项,xargs将把所有ip传递给一个ping命令。
[root@xinbiancheng.cn]$  xargs -a ip.txt -t -L 1 ping -c 1
ping -c 1 114.114.114.114 
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=60 time=30.3 ms

--- 114.114.114.114 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 30.358/30.358/30.358/0.000 ms
ping -c 1 www.xinbiancheng.cn 
PING www.xinbiancheng.cn (39.98.160.179) 56(84) bytes of data.
64 bytes from 39.98.160.179 (39.98.160.179): icmp_seq=1 ttl=64 time=1.07 ms

--- www.xinbiancheng.cn ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.077/1.077/1.077/0.000 ms
ping -c 1 202.102.128.68 
PING 202.102.128.68 (202.102.128.68) 56(84) bytes of data.
64 bytes from 202.102.128.68: icmp_seq=1 ttl=83 time=17.3 ms

--- 202.102.128.68 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 17.381/17.381/17.381/0.000 ms

八、-n 参数

-L参数虽然解决了多行的问题,但是有时用户会在同一行输入多项。

[root@xinbiancheng.cn]$ xargs find -name
"*.txt" "*.md"
find: paths must precede expression: `*.md'

上面的命令将同一行的两项作为命令行参数,导致报错。

-n参数指定每次将多少项,作为命令行参数。

[root@xinbiancheng.cn]$ xargs -n 1 find -name

上面命令指定将每一项(-n 1)标准输入作为命令行参数,分别执行一次命令(find -name)。

下面是另一个例子。

[root@xinbiancheng.cn]$ echo {0..9} | xargs -n 2 echo
0 1
2 3
4 5
6 7
8 9

上面命令指定,每两个参数运行一次echo命令。所以,10个阿拉伯数字运行了五次echo命令,输出了五行。

九、-I 参数

如果xargs要将命令行参数传给多个命令,可以使用-I参数。

-I指定每一项命令行参数的替代字符串。

[root@xinbiancheng.cn]$ cat abc.txt
a
b
c
[root@xinbiancheng.cn]$ cat abc.txt | xargs -I file sh -c 'echo file; mkdir file'
a
b
c
[root@xinbiancheng.cn]$ ls
a  abc.txt  b  c

上面代码中,abc.txt是一个三行的文本文件。我们希望对每一项命令行参数,执行两个命令(echomkdir),使用-I file表示file是命令行参数的替代字符串。执行命令时,具体的参数会替代掉echo file; mkdir file里面的两个file

十、 xargs -E ,有的系统的xargs版本可能是-e  eof-str

该选项指定一个字符串,当xargs解析出多个命令行参数的时候,如果搜索到-e指定的命令行参数,则只会将-e指定的命令行参数之前的参数(不包括-e指定的这个参数)传递给xargs

[root@xinbiancheng.cn]$ echo '11 22 33' | xargs -E '33' echo
11 22

十一、xargs -P 高速并发处理

使用xargs的分批行为,除了可以解决一些问题,还可以一次性将多个分批交给不同进程去处理,这些进程可以使用多个cpu执行,效率可谓大幅提高。

"-P N"选项可以指定并行处理的进程数量为N。不指定"-P"时,默认为1个处理进程,也就是串行执行。指定为0时,将尽可能多地开启进程数量。当xargs正在运行时(也就是还有分批正在处理),可以发送SIGUSR1信号给xargs进程,表示增加一个处理进程,同样,可以向xargs进程发送SIGUSR2进程,表示减少一个处理进程。但需要注意,即使发送SIGUSR2信号,xargs也不会中断正在执行任务的进程,也就是说,在某个进程处理当前分批任务结束之前,不会被中断,只有当前分批任务执行完毕,准备接下一个分批任务时,才会被xargs给杀掉。

例如,一个简单的sleep命令,在不使用"-P"的时候,默认是一个进程按批的先后进行处理:

[root@xinbiancheng.cn]$ time echo {1..4} | xargs -n 1 sleep
 
real    0m10.011s
user    0m0.000s
sys     0m0.011s

总共用了10秒,因为每批传一个参数,第一批睡眠1秒,然后第二批睡眠2秒,依次类推,还有3秒、4秒,共1+2+3+4=10秒。

如果使用-P指定4个处理进程:

[root@xinbiancheng.cn]$ time echo {1..4} | xargs -n 1 -P 4 sleep
 
real    0m4.005s
user    0m0.000s
sys     0m0.007s

结果总共才用了4秒,因为这4个分批同时交给了4个进程同时处理,所以取最长睡眠时间。

以下是一次并行执行过程中,CPU的使用情况:

[root@xinbiancheng.cn]$ ps -eo pid,args,psr,pcpu | grep slee[p]
 25566 xargs -n 1 -P 5 sleep         3  0.0
 25567 sleep 20                      1  0.0
 25568 sleep 21                      2  0.0
 25569 sleep 22                      0  0.0
 25570 sleep 23                      2  0.0
 25571 sleep 24                      3  0.0

在上面的结果中,启动了5个sleep进程,这5个进程分别用了cpu0、cpu1、cpu2和cpu3共4个cpu,因为我的虚拟机只给分配了4核心cpu。

那么,xargs的哪些选项可以通过"-P"的并行能力来提升效率?其实,只要能分批的选项,都可以使用"-P",包括"-n"、"-L"和"-i"。在man xagrs中,只提到了"-n"和"-L"可以结合"-P",并没有提到"-i",但我前面已经验证过了,"-i"其实也是分批行为,也能结合"-P"的并行功能使用。

下面的示例,可以验证"-i -P"结合:

[root@xinbiancheng.cn]$ time echo -n {1..4} | xargs -d" " -i -P 4 sleep {}
 
real    0m4.003s
user    0m0.000s
sys     0m0.005s

如果需要发送信号,来增、减并行进程数量,可以向xargs进程发送SIGUSR1和SIGUSR2信号,例如:

kill -USR1 `pgrep -f "xargs"`

虽然xargs提供了这样的并行处理能力,但说实话,用到的机会并不多,它毕竟只是一个参数传递工具。我也专门写了一篇关于xargs高效并行的文章,另一个gnu工具parallel(装上epel源,yum -y install parallel即可)在并行处理上用的比较多,比如对一个巨大的文件利用sort进行排序时,使用parallel并行排序,可以大幅提升效率,即使sort自身已经足够完美。非常幸运的是,在你把xargs学到了这里,使用parallel将非常简单,因为它的选项和行为模式和xargs是基本一致的。

十二、--max-procs 参数

xargs默认只用一个进程执行命令。如果命令要执行多次,必须等上一次执行完,才能执行下一次。

--max-procs参数指定同时用多少个进程并行执行命令。--max-procs 2表示同时最多使用两个进程,--max-procs 0表示不限制进程数。

[root@xinbiancheng.cn]$ docker ps -q | xargs -n 1 --max-procs 0 docker kill

上面命令表示,同时关闭尽可能多的 Docker 容器,这样运行速度会快很多。

Linux 命令大全