xargs是GNU fileutils提供的一个命令,fileutils包含find, locate,xargs和updatedb。
xargs的作用是读取标准输入,构造成命令行参数,并执行命令。通常配合find的输出使用。
xargs [option…] [command [initial-arguments]]
所谓命令行参数的形式如下:
cp a.c b.c
其中cp
是命令,其余为参数,以空格分隔。
那么什么时候需要xargs呢?比如有如下需求,将a目录下的所有*.c
文件复制到b目录。我们
首先需要找出所有的*.c
文件,比如可以通过find a -name "*.c"
命令。这个命令输出结果是一
行行的文件名,如:
./src/a.c
./src/test/b.c
./src/example/c.c
...
很自然的,我们会想到,如果可以把输出转成一行,我们就能直接使用cp命令将多个文件复制
到b目录了。即cp ./src/a.c ./src/test/b.c ./src/example/c.c ./b
。
查看xargs命令的格式:xargs [options] [command [initial-arguments]]
。xargs的语义就是将输入转换
成command
的参数。按照这个格式,我们先写下:
find a -name "*.c" | xargs echo
翻译一下就是将find的输出(多行文件名)转换成echo命令的参数(以空格为区分)。相当于:
echo ./src/a.c ./src/test/b.c ./src/example/c.c
直接将echo换成cp命令还存在一个问题,xargs是将参数附在command
的后面,但是cp需要将参数
加在cp和目标目录的中间,即cp xxx ./b
。好在xargs提供了-I
参数,允许替换命令位置:
find a -name "*.c" | xargs -I {} cp {} ./b
其中{}
相当于占位符,告诉xargs将参数放在这里,也可以用任意其他字符串代替。
执行完后,文件确实正确复制到了b目录,但是实际过程却有些不一样。由于使用了-I
参数,导致
xargs每次读1行,上述命令并不是用cp将多个文件复制到b目录,而是执行了多次cp命令,每次只
复制一个文件。
为什么会这样呢?回到一开始说的,xargs的作用是将输入转换成命令行参数,而转换的方式是由
一系列参数控制的。比如:
-d
:指定分隔符,默认是换行符\n
,这也是多行输入被转换成了一行的原因。我们也可以指定其他
的分隔符号。-n
:指定数量,即一组包含多少个参数。比如如果指定n等于2,那么相当于每次只有两个参数被
提取。如果参数大于2,那么会重复执行command
命令。
那如果我就要一次性读入所有参数怎么做呢?可以通过多个xargs组合来实现。
find a -name "*.c" | xargs | xargs -I x cp x ./b
第一次xargs将所有行转换成空格分隔的一行,然后第二次xargs全部读入,替换cp的参数x
。