xargs的原理剖析及用法详解(2)

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 -n

xargs -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是指定传递的最大段数,也就是分批。

xargs的原理剖析及用法详解

[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 -I

xargs -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

?...

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/13325.html