xargs原理剖析及用法详解
概述
学习这个xargs花了比较多时间,在网上翻了很久也查了很多书关于xargs的介绍,都只是简单的介绍了它的几个用法,却没有介绍它工作的原理,个人感觉xargs的基本用法很简单,它的选项实现的功能也很简单,但是多个选项配合时问题就变得很难,经常发现结果和预期不同。
1、为什么需要xargs
管道实现的是将前面的stdout作为后面的stdin,但是有些命令不接受管道的传递方式,最常见的就是ls命令。有些时候命令希望管道传递的是参数,但是直接用管道有时无法传递到命令的参数位,这时候需要xargs,xargs实现的是将管道传输过来的stdin进行处理然后传递到命令的参数位上。
也就是说xargs完成了两个行为:处理管道传输过来的stdin;将处理后的传递到正确的位置上。
可以试试运行下面的几条命令,应该能很好理解xargs的作用了:
# echo "/etc/inittab" | cat # 直接将标准输入的内容传递给cat
# echo "/etc/inittab" | xargs cat # 将标准输入的内容经过xargs处理后传递给cat
# find /etc -maxdepth 1 -name "*.conf" -print0 | xargs -0 -i grep "hostname" -l {}
# 将搜索的文件传递给grep的参数位进行搜索,若不使用xargs,则grep将报错
xargs的作用不仅仅限于简单的stdin传递到命令的参数位,它还可以将stdin或者文件stdin分割成批,每个批中有很多分割片段,然后将这些片段按批交给xargs后面的命令进行处理。
通俗的讲就是原来只能一个一个传递,分批可以实现10个10个传递,每传递一次,xargs后面的命令处理这10个中的每一个,处理完了处理下一个传递过来的批次,如下图。
说明:尽管实现了分批处理,但是默认情况下并没有提高任何效率,因为分批传递之后还是一次执行一个。而且有时候分批传递后是作为一个参数的整体,并不会将分批中的信息分段执行。xargs提供了"-P"选项,用于指定并行执行的数量(默认只有一个处理进程,不会提升效率,可以指定为N个子进程,或者指定为0表示尽可能多地利用CPU),这样就能让分批操作更好地利用多核cpu,从而提升效率。
这里已经暗示了xargs处理的优先级或顺序了:先分割,再分批,然后传递到参数位。
分割有三种方法:独立的xargs、xargs -d和xargs -0。后两者可以配合起来使用,之所以不能配合独立的xargs使用,答案是显然的,指定了-d或-0选项意味着它不再是独立的。
2、 文本意义上的符号和标记意义上的符号
一个贯穿xargs命令的符号分类:文本意义上的空格、制表符、反斜线、引号和非文本意义上的符号。
文本意义上的空格、制表符、反斜线、引号:未经处理就已经存在的符号,例如文本的内容中出现这些符号以及在文件名上出现了这些符号都是文本意义上的。
与之相对的是非文本意义的符号:处理后出现的符号,例如ls命令的结果中每个文件之间的制表符,它原本是不存在的,只是ls命令处理后的显示方式。还包括每个命令结果的最后的换行符,文件内容的最后一行结尾的换行符。
3、xargs的一些有用的选项
1. -d 选项
默认情况下xargs将其标准输入中的内容以空白(包括空格、Tab、回车换行等)分割成多个之后当作命令行参数传递给其后面的命令,并运行之,我们可以使用 -d 命令指定分隔符,例如:
#echo '11@22@33' | xargs echo
11@22@33
默认情况下以空白分割,那么11@22@33这个字符串中没有空白,所以实际上等价于 echo 11@22@33 其中字符串 '11@22@33' 被当作echo命令的一个命令行参数
#echo '11@22@33' | xargs -d '@' echo
11 22 33
指定以@符号分割参数,所以等价于 echo 11 22 33 相当于给echo传递了3个参数,分别是11、22、33
2. -p 选项
使用该选项之后xargs并不会马上执行其后面的命令,而是输出即将要执行的完整的命令(包括命令以及传递给命令的命令行参数),询问是否执行,输入 y 才继续执行,否则不执行。这种方式可以清楚的看到执行的命令是什么样子,也就是xargs传递给命令的参数是什么,例如:
#echo '11@22@33' | xargs -p -d '@' echo
echo 11 22 33
?...y ==>这里询问是否执行命令 echo 11 22 33 输入y并回车,则显示执行结果,否则不执行
11 22 33 ==>执行结果
3. -n 选项
该选项表示将xargs生成的命令行参数,每次传递几个参数给其后面的命令执行,例如如果xargs从标准输入中读入内容,然后以分隔符分割之后生成的命令行参数有10个,使用 -n 3 之后表示一次传递给xargs后面的命令是3个参数,因为一共有10个参数,所以要执行4次,才能将参数用完。例如:
#echo '11@22@33@44@55@66@77@88@99@00' | xargs -d '@' -n 3 echo
11 22 33
44 55 66
77 88 99
00
等价于:
echo 11 22 33
echo 44 55 66
echo 77 88 99
echo 00
实际上运行了4次,每次传递3个参数,最后还剩一个,就直接传递一个参数。
4. -E 选项,有的系统的xargs版本可能是-e eof-str
该选项指定一个字符串,当xargs解析出多个命令行参数的时候,如果搜索到-e指定的命令行参数,则只会将-e指定的命令行参数之前的参数(不包括-e指定的这个参数)传递给xargs后面的命令
#echo '11 22 33' | xargs -E '33' echo
11 22
4、分割行为之:xargs
# cd /tmp
# rm -fr *
# mkdir a b c d test logdir shdir
# touch "one space.log"
]# touch logdir/{1..10}.log
# touch shdir/{1..5}.sh
# echo "the second sh the second line" > shdir/2.sh
# cat <<EOF>shdir/1.sh
> the first sh
> the second line
> EOF
对于xargs,它将接收到的stdout处理后传递到xargs后面的命令参数位,不写命令时默认的命令是echo。
# cat shdir/1.sh | xargsthe first sh the second line
# cat shdir/1.sh | xargs echo
the first sh the second line
将分行处理掉不是echo实现的,而是管道传递过来的stdin经过xargs处理后的:将所有空格、制表符和分行符都替换为空格并压缩到一行上显示,这一整行将作为一个整体,这个整体的所有空格属性继承xargs处理前的符号属性,即原来是文本意义的或标记意义的在替换为空格后符号属性不变。这个整体可能直接交给命令或者作为stdout通过管道传递给管道右边的命令,这时结果将作为整体传递,也可能被xargs同时指定的分批选项分批处理。
# find /tmp -maxdepth 1 | xargs ls
如果对独立的xargs指定分批选项,则有两种分批可能:指定-n时按空格分段,然后划批,不管是文本意义的空格还是标记意义的空格,只要是空格都是-n的操作对象;指定-L或者-i时按段划批,文本意义的符号不被处理。
# ls #one space.log是一个文件的文件名,只是包含了空格
a b c d logdir one space.log shdir sh.txt test vmware-root x.txt
# ls | xargs -n 2
# ls | xargs -L 2
# ls | xargs -i -p echo {}
5、使用xargs -p或xargs -t观察命令的执行过程
使用-p选项是交互询问式的,只有每次询问的时候输入y(或yes)才会执行,直接按enter键是不会执行的。
使用-t选项是在每次执行xargs后面的命令都会先在stderr上打印一遍命令的执行过程然后才正式执行。
使用-p或-t选项就可以根据xargs后命令的执行顺序进行推测,xargs是如何分段、分批以及如何传递的,这通过它们有助于理解xargs的各种选项。
# ls | xargs -n 2 -t
/bin/echo a b # 先打印一次命令,表示这一次只echo两个参数:a和b
a b
/bin/echo c d # 表示这次只打印c和d
c d
/bin/echo logdir one
logdir one
/bin/echo space.log shdir
space.log shdir
/bin/echo sh.txt test
sh.txt test
/bin/echo vmware-root
vmware-root
# ls | xargs -n 2 -p
/bin/echo a b ?...y # 询问是否echo a b
/bin/echo c d ?...a b
y # 询问是否echo c d,后面的...a b指示了echo c d是在前一个结果的基础上接着执行的
/bin/echo logdir one ?...c d
y
/bin/echo space.log shdir ?...logdir one
y
/bin/echo sh.txt test ?...space.log shdir
y
/bin/echo vmware-root ?...sh.txt test
y
vmware-root
从上面的-t和-p的结果上都可以知道每次传递两个参数。
浙公网安备 33010602011771号