Xargs命令详解

摘要:在使用命令行时,有时我们希望把某些命令的输出传入到其他命令中去,但是并不是所有的命令都能使用管道 | 来完成这个操作,这时候 Xargs 就派上用场。Xargs 可以算是命令组合的神器。

由一个问题开始

在一个文件夹下有若干文件和子文件夹,希望将除去某个子文件夹外的所有文件和文件夹移动到该子文件夹中。 [后来找到其他的解决方法:https://askubuntu.com/a/91743/582639]

其中的一种思路就是,首先将需要移动的文件和文件夹 list 出来,然后在将这些 item 一一传入到 mv 命令中,完成移动操作。这里有几个问题需要解决:

  1. list 需要过滤目标文件夹
  2. 移动需要传参数
  3. 移动是一个循环

命令

1
ls | grep -v dist | xargs -I file mv file dist
  1. 首先通过 ls + grep,使用 -v 参数排除掉 dist
  2. 然后通过 xargs 将数据结果循环赋值给 file,其中 -I 参数就是实现依次处理功能
  3. 移动文件到指定文件夹

-I replstr
Execute utility for each input line, replacing one or more occurrences of replstr in
up to replacements (or 5 if no -R flag is specified) arguments to utility with the
entire line of input. The resulting arguments, after replacement is done, will not be
allowed to grow beyond 255 bytes; this is implemented by concatenating as much of the
argument containing replstr as possible, to the constructed arguments to utility, up
to 255 bytes. The 255 byte limit does not apply to arguments to utility which do not
contain replstr, and furthermore, no replacement will be done on utility itself.
Implies -x.

基本用法

参考1参考2

-0 处理特殊字符

-0 :当sdtin含有特殊字元时候,将其当成一般字符,想/‘空格等

1
2
3
4
$ echo "/ /  "|xargs echo
/ /
$ echo "/ / "|xargs -0 echo
/ /

-a 文件读取

-a file 从文件中读入作为sdtin

1
2
3
4
5
$ cat 1.txt 
aaa bbb ccc ddd
a b
$ xargs -a 1.txt echo
aaa bbb ccc ddd a b

-e 使用 eofstr 作为 EOF逻辑标记

-e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。

1
2
3
4
5
$ xargs -E 'ddd'  -a 1.txt echo
aaa bbb ccc

$ cat 1.txt |xargs -E 'ddd' echo
aaa bbb ccc

-n 执行次数

-n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。

1
2
3
4
$ cat 1.txt |xargs -n 2 echo
aaa bbb
ccc ddd
a b

-p 交互性

-p 操作具有可交互性,每次执行comand都交互式提示用户选择,当每次执行一个argument的时候询问一次用户

1
2
3
4
5
$ cat 1.txt |xargs -p echo
echo aaa bbb ccc ddd a b ?...y
aaa bbb ccc ddd a b
$ cat 1.txt |xargs -p echo
echo aaa bbb ccc ddd a b ?...n

-t 表示先打印命令,然后再执行。

1
2
3
$ cat 1.txt |xargs -t echo
echo aaa bbb ccc ddd a b
aaa bbb ccc ddd a b

-i -I 遍历输出行

-i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给{},可以用{}代替。

1
2
3
4
5
6
7
8
$ ls
1.txt 2.txt 3.txt log.xml
$ ls *.txt |xargs -t -i mv {} {}.bak
mv 1.txt 1.txt.bak
mv 2.txt 2.txt.bak
mv 3.txt 3.txt.bak
$ ls
1.txt.bak 2.txt.bak 3.txt.bak log.xml

注意,-I 必须指定替换字符 -i 是否指定替换字符-可选

1
find . | xargs -I {} cp {} $D_PATH

1
find . | xargs -i cp {} $D_PATH

注意:cshell和tcshell中,需要将{}用单引号、双引号或反斜杠,否则不认识。bash可以不用。
find /shell -maxdepth 2 -name a -print | xargs -t -i sed -i ‘1 i\111’ ‘{}‘
-r no-run-if-empty 如果没有要处理的参数传递给xargsxargs 默认是带 空参数运行一次,如果你希望无参数时,停止 xargs,直接退出,使用 -r 选项即可,其可以防止xargs 后面命令带空参数运行报错。

1
2
3
4
5
$ echo ""|xargs -t mv
mv
mv: missing file operand
Try `mv --help' for more information.
$ echo ""|xargs -t -r mv #直接退出

-s 最大命令行字符数

-s num xargs后面那个命令的最大命令行字符数(含空格)

1
2
3
4
5
6
7
8
9
10
$ cat 1.txt.bak |xargs  -s 9 echo
aaa
bbb
ccc
ddd
a b
$ cat 1.txt.bak |xargs -s 4 echo
xargs: can not fit single argument within argument list size limit #length(echo)=4
$ cat 1.txt.bak |xargs -s 8 echo
xargs: argument line too long #length(echo)=4,length(aaa)=3,length(null)=1,total_length=8

-L 从输入一次读取行数

-L 从标准输入一次读取num行送给Command命令 ,-l和-L功能一样

1
2
3
4
5
6
7
8
9
10
11
12
$ cat 1.txt.bak 
aaa bbb ccc ddd
a b
ccc
dsds
$ cat 1.txt.bak |xargs -L 4 echo
aaa bbb ccc ddd a b ccc dsds
$ cat 1.txt.bak |xargs -L 1 echo
aaa bbb ccc ddd
a b
ccc
dsds

-d 分隔符

-d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符

1
2
3
4
5
6
7
$ cat 1.txt.bak 
aaa@ bbb ccc@ ddd
a b

$ cat 1.txt.bak |xargs -d '@' echo
aaa bbb ccc ddd
a b

-x 退出

-x exit的意思,如果有任何 Command 行大于 -s Size 标志指定的字节数,停止运行 xargs 命令,-L -I -n 默认打开-x参数,主要是配合-s使用
-P 修改最大的进程数,默认是1,为0时候为as many as it can 。

使用案例

选项非常多,一般我们需要时可以在查看 man 手册,下面我们看看有哪些常用的使用案例

用rm 删除太多的文件时候,可能得到一个错误信息:/bin/rm Argument list too long. 用xargs去避免这个问题:

1
find . -type f -name "*.log" -print0 | xargs -0 rm -f

xargs -0将\0作为定界符。

统计一个源代码目录中所有php文件的行数:

1
find . -type f -name "*.php" -print0 | xargs -0 wc -l

查找所有的jpg 文件,并且压缩它们:

1
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz

xargs其他应用

假如你有一个文件包含了很多你希望下载的URL,你能够使用xargs下载所有链接:

1
cat url-list.txt | xargs wget -c

1、在当前目录下查找所有用户具有读、写和执行权限的文件,并收回相应的写权限:

1
# find . -perm -7 -print | xargs chmod o-w

2、查找系统中的每一个普通文件,然后使用xargs命令来测试它们分别属于哪类文件

1
2
# find . -type f -print | xargs file
./liyao: empty

3、尝试用rm 删除太多的文件,你可能得到一个错误信息:/bin/rm Argument list too long. 用xargs 去避免这个问题

1
$find ~ -name ‘*.log’ -print0 | xargs -i -0 rm -f {}

4、查找所有的jpg 文件,并且压缩它

1
# find / -name *.jpg -type f -print | xargs tar -cvzf images.tar.gz

5、拷贝所有的图片文件到一个外部的硬盘驱动

1
# ls *.jpg | xargs -n1 -i cp {} /external-hard-drive/directory

开始问题的其他解法

使用 ext 和 dot glob 来快速完成

1
2
3
shopt -s extglob dotglob
mv !(new) new
shopt -u dotglob

其中 extglob 为了支持 ! 语法
其中 dotglob 为了支持处理所有的隐藏文件 (dot prefix file name)