1.6 分割行为之:xargs -0
xargs -0的行为和xargs -d基本是一样的,只是-d是指定分隔符,-0是指定固定的\0作为分隔符。其实xargs -0就是特殊的xargs -d的一种,它等价于xargs -d"\0"。
xargs -0行为如下:
? xargs -0是分割阶段的选项,所以它优先于分批选项(-n、-L、-i)。
? xargs -0不是先xargs再-0处理的,它是区别于独立的xargs的另一个分割选项。
? xargs -0可以处理接收的stdin中的null字符(\0)。如果不使用-0选项或- -null选项,检测到\0后会给出警告提醒,并只向命令传递非\0段。xargs -0和- -null是一样的效果。
xargs -0整体执行有几个阶段:
? 替换:将接收stdin的所有的标记意义的符号替换为\n,替换完成后所有的符号(空格、制表符、分行符)变成字面意义上的普通符号,即文本意义的符号。
? 分段:将检测到的null字符(\0)使用标记意义上的空格来分段,由于分段前所有符号都是普通字面意义上的符号,所以有的分段中可能包含了空格、制表符、分行符。也就是说除了-0导致的分段空格,其余所有的符号都是分段中的一部分。
如果没有检测到\0,则接收的整个stdin将成为一个不可分割的整体,任何分批选项都不会将其分割开,因为它只有一个段。
? 输出:最后根据指定的分批选项来输出。这里需要注意,分段前后有特殊符号时会完全按照符号输出。
根据上面的结论可知,xargs -0会忽略所有文本意义上的符号,它的主要目的是处理\0符号。
[root@linuxidc tmp]# touch "one space.log" [root@linuxidc tmp]# ls | tr " " "\t" | xargs -0 #忽略文本意义上的制表符
a
b
c
d
logdir
one space.log
shdir
sh.txt
test
vmware-root
# 注意有空行,因为命令结尾是一个标记意义上换行符号
[root@linuxidc tmp]# ls | tr " " " " | xargs -0 #忽略文本意义上的空格
a
b
c
d
logdir
one space.log
shdir
sh.txt
test
vmware-root
# 注意有空行
如果检测到\0而没有使用-0或--null处理则给出警告。注意警告后执行哪些文件。
[root@linuxidc tmp]# ls | tr " " "\0"
a
b
c
d
logdir
onespace.log # 这里实际上是one\0space.log
shdir
sh.txt
test
vmware-root
[root@linuxidc tmp]# ls | tr " " "\0" | xargs
xargs: Warning: a NUL character occurred in the input. It cannot be passed through in the argument list. Did you mean to use the --null option?
a b c d logdir one shdir sh.txt test vmware-root # 执行时将space.log忽略了,其余都执行
再例如,将所有的换行符换成null字符,结果中除了最前面的字母a和由于空格而不被\0影响的space.log,其余的由于全部有\0全部被忽略。
[root@linuxidc tmp]# ls | tr "\n" "\0"
abcdlogdirone space.logshdirsh.txttestvmware-root # 只有a的前面和space.log的前面是没有\0的
[root@linuxidc tmp]# ls | tr "\n" "\0" | xargs
xargs: Warning: a NUL character occurred in the input. It cannot be passed through in the argument list. Did you mean to use the --null option?
a space.log # 所以只执行这两个
使用-0或--null来解决问题,也可以使用等价的xargs -d"\0"来解决。
[root@linuxidc tmp]# ls | tr "\n" "\0" | xargs -0 或者 [root@linuxidc tmp]# ls | tr "\n" "\0" | xargs -d"\0"
a b c d logdir one space.log shdir sh.txt test vmware-root
如果使用xargs -0时不指定分批选项(-n、-L、-i),则处理后的结果将作为一个整体输出。
如果指定了分批选项,并且检测到了null字符,则以\0位的空格分段划批,这时使用-n、-L或-i的结果是一样的。例如使用-n选项来观察是如何分批的。
[root@linuxidc tmp]# ls | tr "\n" "\0" | xargs -0 -n 3
a b c
d logdir one space.log
shdir sh.txt test
vmware-root x.txt
如果指定了分批选项,但没有检测到null字符,则整个结果将称为一个不可分割整体,这时使用分批选项是完全无意义的。
[root@linuxidc tmp]# ls | xargs -0 -n 3 -p
/bin/echo a
b
c
d
logdir
one space.log
shdir
sh.txt
test
vmware-root
x.txt
?...
1.7 分批行为分批用于指定每次传递多少个分段。有三种分批选项:-n,-L和-i。在本文的开头已经指明了为什么-i是分批选项,但是这里还是要介绍它逻辑上定义的功能:参数替换。
既然三种选项都是分批选项,如果在一个xargs中使用了多个分批选项,则它们之间必然会冲突,这时就需要依赖它们的优先级来决定选用哪个选项。在介绍完三种分批选项后我会证明它们的优先级。
1.7.1 xargs -nxargs -n分两种情况:和独立的xargs一起使用,这时按照每个空格分段划批;和xargs -d或xargs -0一起使用,这时按段分批,即不以空格、制表符和分行符分段划批。
[root@linuxidc tmp]# ls | xargs -n 3 -p #和独立的xargs一起使用,以空格分段划批
/bin/echo a b c ?...
/bin/echo d logdir one ?... # one和space.log被割开了
/bin/echo space.log shdir sh.txt ?...
/bin/echo test vmware-root x.txt ?...
/bin/echo ?...
[root@linuxidc tmp]# ls | xargs -d"o" -n 3 -p # 和xargs -d一起使用,按段分批
/bin/echo a
b
c
d
l gdir
ne space.l ?...
/bin/echo g
shdir
sh.txt
test
vmware-r t
x.txt
?...
/bin/echo ?...
1.7.2 xargs -L和-n选项类似,唯一的区别是-L永远是按段划批,而-n在和独立的xargs一起使用时是按空格分段划批的。
该选项的一个同义词是-l,但是man推荐使用-L替代-l,因为-L符合POSIX标准,而-l不符合。使用--max-lines也可以。
也许你man xargs时发现-L选项是指定传递时最大传递行数量的,man的结果如下图。但是通过下面的实验可以验证其实-L是指定传递的最大段数,也就是分批。
[root@linuxidc tmp]# ls | xargs -L 3 -p #如果是指定传递的最大行数量,则一行就输出完了,这里却分了多行输出
/bin/echo a b c ?...
/bin/echo d logdir one space.log ?... # 这里可以证明-L和-n的区别
/bin/echo shdir sh.txt test ?...
/bin/echo vmware-root x.txt ?...
[root@linuxidc tmp]# ls | xargs -d"o" -L 3 -p # 这就更能证明是指定最大传递的段数量了
/bin/echo a
b
c
d
l gdir
ne space.l ?...
/bin/echo g
shdir
sh.txt
test
vmware-r t
x.txt
?...
1.7.3 xargs -i和xargs -Ixargs -i选项在逻辑上用于接收传递的分批结果。
如果不使用-i,则默认是将分割后处理后的结果整体传递到命令的最尾部。但是有时候需要传递到多个位置,不使用-i就不知道传递到哪个位置了,例如重命名备份的时候在每个传递过来的文件名加上后缀.bak,这需要两个参数位。
使用xargs -i时以大括号{}作为替换符号,传递的时候看到{}就将被结果替换。可以将{}放在任意需要传递的参数位上,如果多个地方使用{}就实现了多个传递。
xargs -I(大写字母i)和xargs -i是一样的,只是-i默认使用大括号作为替换符号,-I则可以指定其他的符号、字母、数字作为替换符号,但是必须用引号包起来。man推荐使用-I代替-i,但是一般都图个简单使用-i,除非在命令中不能使用大括号,如touch {1..1000}.log时大括号就不能用来做替换符号。
例如下面的重命名备份过程。
[root@linuxidc tmp]# ls logdir/
10.log 1.log 2.log 3.log 4.log 5.log 6.log 7.log 8.log 9.log
[root@linuxidc tmp]# ls logdir/ | xargs -i mv ./logdir/{} ./logdir/{}.bak # 将分段传递到多个参数位 [root@linuxidc tmp]# ls logdir/
10.log.bak 1.log.bak 2.log.bak 3.log.bak 4.log.bak 5.log.bak 6.log.bak 7.log.bak 8.log.bak 9.log.bak
但是我将“-i”选项划分在分批选项里,它默认一个段为一个批,每次传递一个批也就是传递一个段到指定的大括号{}位上。在稍后的分批选项优先级部分我会给出我的理由。
由于-i选项是按分段来传递的。所以尽管看��去等价的xargs echo和xargs -i echo {}并不等价。
[root@linuxidc tmp]# ls | xargs echo
a b c d logdir one space.log shdir sh.txt test vmware-root
[root@linuxidc tmp]# ls | xargs -i echo {}
a
b
c
d
logdir
one space.log
shdir
sh.txt
test
vmware-root
既然使用-i后是分段传递的,这就意味着指定了它就无法实现按批传递多个参数了;并且如果使用多个大括号,意味着必须使用-i,那么也无法分批传递。
例如,想将数字1-10每3个数显示在start和end之间。效果如下:
start 1 2 3 end
start 4 5 6 end
start 7 8 9 end
start 10 end
由于指定了参数传递位置,所以必须使用-i,那么就无法一次传递3个数。要解决这个问题,就要想办法让每三个数分一次段然后使用-i传递,方法也就随之而来了。可以将每三个数分一次行写入一个文件。如:
[root@linuxidc tmp]# cat <<eof>logdir/1.log > 1 2 3 > 4 5 6 > 7 8 9 > 10 > eof
再使用xargs -i分批传递。
[root@linuxidc tmp]# cat logdir/1.log | xargs -i echo "start {} end"
start 1 2 3 end
start 4 5 6 end
start 7 8 9 end
start 10 end
也可以使用多次xargs。很多时候无法解决分段的问题都可以通过多次使用xargs来解决。
[root@linuxidc tmp]# echo {1..10} | xargs -n 3 | xargs -i echo "start {} end"
由于xargs -i传递数据时是在shell执行xargs命令的时候,根据shell解析命令行的流程,xargs后的命令如果有依赖于待传递数据的表达式,则很可能无法正确执行。例如,无法通过xargs传递数值做正确的算术扩展:
[root@linuxidc logdir]# echo 1 | xargs -I "x" echo $((2*x))
0
无法将数据传递到命令替换中。
[root@linuxidc ~]# echo /etc/fstab | xargs -i `cat {}` cat: {}: No such file or directory
这时要通过xargs正确实现目标,只能改变方法或找小技巧,例如:
[root@linuxidc ~]# echo 1 | xargs -i expr 2 \* {} # 感谢楼下评论者提供的expr思路 2 [root@linuxidc ~]# echo /etc/fstab | xargs -i cat $(echo {})
另外,xargs无法处理bash内置命令。例如:
[root@linuxidc ~]# echo /etc | xargs -i cd {} xargs: cd: No such file or directory
这是xargs的限制和缺点,而不是"-i"选项的限制。
1.7.4 分批选项的优先级-i选项优先级最高,-L选项次之,-n选项优先级最低。当然,如果什么分批选项也不指定,肯定是不指定优先级最低(这是废话?)。
多个分批选项同时指定时,高优先级的选项会覆盖低优先级的选项。也就是说这时候指定低优先级的选项是无意义的。
下面给出证明。
先证明-i选项优先级最高。
[root@linuxidc tmp]# ls | xargs -d"o" -n 2 -p -i echo {}
echo a
b
c
d
l ?... # 说明是一段一段输出,而不是两段一批输出,即-n选项被忽略
echo gdir
?...
echo ne space.l ?...
echo g
shdir
sh.txt
test
vmware-r ?...
echo ?...
echo t
x.txt
?...
[root@linuxidc tmp]# ls | xargs -d"o" -L 3 -i -p echo {} # 和上面的结果是一模一样的,说明-L选项被忽略
echo a
b
c
d
l ?...
echo gdir
?...
echo ne space.l ?...
echo g
shdir
sh.txt
test
vmware-r ?...
echo ?...
echo t
x.txt
?...
再证明-L选项优先级高于-n。
[root@linuxidc tmp]# ls | xargs -d"o" -n 2 -p -L 1 echo # 结果也是一段一段输出的,说明-n选项被忽略
echo a
b
c
d
l ?...
echo gdir
?...
echo ne space.l ?...
echo g
shdir
sh.txt
test
vmware-r ?...
echo ?...
echo t
x.txt
?...