C语言再学习 -- Xargs用法详解

时间:2022-05-24 09:19:40

参看:Xargs用法详解(原创)

简介

之所以能用到这个命令,关键是由于很多命令不支持管道来来传递参数,而日常工作中有这个必要,所以就有了 xargs 命令,例如:

这个命令是错误的:

find /sbin -pern +700 | ls -l

这样才是正确的

find /sbin -perm +700 | xargs ls -l

xargs 可以读入 stdin 的资料,并且以空白字元(所谓字元就是字节)或断行字元作为分辨,将 stdin 的资料分隔成为 arguments。因为是以空白字元作为分隔,所以,如果一些档名或者是其他意义的名词内含有空白字元的时候,xargs 可能就会误判了,如果需要处理特殊字符,需要使用 -0 参数进行处理.


选项解释:

-0 :当stdin含有特殊字元时,将其当做一般字符,像 '/' 空格等

root@zslf-virtual-machine:/mnt/test# echo "//" | xargs echo
//
root@zslf-virtual-machine:/mnt/test# echo "//" | xargs -0 echo
//

-a:file从文件中读入作为 stdin

root@zslf-virtual-machine:/mnt/test# cat abc.txt 
aaa bbb ccc ddd
a b c
root@zslf-virtual-machine:/mnt/test# xargs -a abc.txt
aaa bbb ccc ddd a b c


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

root@zslf-virtual-machine:/mnt/test# xargs -a abc.txt -E "ccc"
aaa bbb
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -E "ccc"
aaa bbb
//不是空格分隔的标志,无法识别
root@zslf-virtual-machine:/mnt/test# xargs -a abc.txt -E "cc"
aaa bbb ccc ddd a b c


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

root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -n 2 
aaa bbb
ccc ddd
a b
c


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

root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -p 
/bin/echo aaa bbb ccc ddd a b c ?...y
aaa bbb ccc ddd a b c
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -p
/bin/echo aaa bbb ccc ddd a b c ?...n


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

root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -t
/bin/echo aaa bbb ccc ddd a b c
aaa bbb ccc ddd a b c


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

root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt
root@zslf-virtual-machine:/mnt/test# ls *.txt | xargs -i mv {} {}.bak
root@zslf-virtual-machine:/mnt/test# ls
abc.txt.bak def.txt.bak

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

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

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 如果没有要处理的参数传递给xargs,xargs 默认是带空参数运行一次,如果你希望无参数时,停止 xargs,直接退出,使用 -r 选项即可,其可以防止xargs 后面命令带空参数运行报错。

root@zslf-virtual-machine:/mnt/test# echo "" | xargs -t mv
mv
mv: 缺少了文件操作数
Try 'mv --help' for more information.
root@zslf-virtual-machine:/mnt/test# echo "" | xargs -t -r mv
(直接退出)


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

root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -s 9 echo
aaa
bbb
ccc
ddd
a b
c
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -s 8 echoxargs: 参数行过长root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -s 3 echoxargs: 无法使用合适的带参数列表大小限制的单一参数
因为,#length(echo)=4,length(aaa)=3,length(null)=1,total_length=8

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

root@zslf-virtual-machine:/mnt/test# cat abc.txt 
aaa bbb ccc ddd
a b c
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -L 4 echo
aaa bbb ccc ddd a b c
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -L 1 echo
aaa bbb ccc ddd
a b c


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

root@zslf-virtual-machine:/mnt/test# cat abc.txt 
aaa@bbb ccc@ddd
a b c
root@zslf-virtual-machine:/mnt/test# cat abc.txt | xargs -d '@' echo
aaa bbb ccc ddd
a b c


-x :exit的意思,如果在任何command行大于 -s size标志指定的字节数,停止运行xargs命令,-L -i -n默认打开-x参数,主要是配合-s使用。


-P:修改最大的进程数,默认是1,为 0 时为 as many as it can。


xargs 和 find

在使用 find 命令的 -exec 选项处理匹配到的文件时,find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能传递给exec的命令长度有限制。这样在find命令运行几分钟后,就会出现溢出错误。错误信息通畅是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。find命令把匹配到的文件传递给 xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。

在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;而使用xargs命令则只有一个进程。另外,在使用xargs命令时,究竟是一次获取所有的参数,还是分批获取参数,以及每一次获取参数的数目,都会根据该命令的选项及系统内核中相应的可调参数来确定。

管道是把一个命令的输出传递给另一个命令作为输入,比如:command1 | command2,但是command2仅把输出的内容作为输入参数。find ./ -name "install.log" -print 打印出的是install.log这个字符串,如果仅仅使用管道,那么command2能够使用的仅仅是install.log这个字符串,不能把它当做文件来进行处理。

root@zslf-virtual-machine:/mnt/test# find . -name "install.log" -print
./install.log

当然这个command2除了xargs。xargs就是为了能够对find搜索到的文件进行操作而编写的。它能把管道传来的字符串当作文件交给其后的命令执行。
举个例子:

root@zslf-virtual-machine:/mnt/test# find . -name "install.log" -print | cat
./install.log
#显示从管道传来的内容,仅仅作为字符串来处理
root@zslf-virtual-machine:/mnt/test# find . -name "install.log" -print | xargs cat
aaaaaaa
#将管道传来的内容作为文件,交给cat执行。也就是说,该命令执行的是如果存在install.log,那么就打印出这个文件的内容。


来看看xargs命令是如何同find命令一起使用的,并给出一些例子。

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

root@zslf-virtual-machine:/mnt/test# find ./ -perm +777  | xargs chmod 700 
root@zslf-virtual-machine:/mnt/test# ls -la
总用量 16
drwx------ 2 zslf zslf 4096 11月 25 14:04 .
drwxr-xr-x 5 root root 4096 11月 24 17:20 ..
-rwx------ 1 root root 22 11月 25 13:48 abc.txt
-rwx------ 1 root root 0 11月 25 13:27 def.txt
-rwx------ 1 root root 8 11月 25 14:04 install.log


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

root@zslf-virtual-machine:/mnt/test# find -type f | xargs file
./def.txt: empty
./abc.txt: ASCII text
./install.log: ASCII text


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

root@zslf-virtual-machine:/mnt/test# ls
abc.txt dd.log def.txt kk.log sd.log
root@zslf-virtual-machine:/mnt/test# find ./ -name "*.log" | xargs rm -r
root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt


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

root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg hh.jpg sd.jpg
root@zslf-virtual-machine:/mnt/test# find ./ -name "*.jpg" -type f | xargs tar -zcvf images.tar.gz
./ef.jpg
./hh.jpg
./sd.jpg
root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg


5、查找所有jpg文件,并重命名

root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg
root@zslf-virtual-machine:/mnt/test# find ./ -name "*.jpg" -type f | xargs -i cp {} {}.old
root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old

或使用 -exec

root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg
root@zslf-virtual-machine:/mnt/test# find ./ -name "*.jpg" -exec cp {} {}.old \;
root@zslf-virtual-machine:/mnt/test# ls
abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old