三分薄地,认真耕耘

导航

 

1、为什么需要xargs命令

1.1 管道“|“的缺陷

管道实现的是将前面的输出stdout作为后面的输入stdin,但是有些命令不接受管道的传递方式。例如:ls,这是为什么呢?

因为有些命令希望管道传递过来的是参数,但是直接使用管道有时无法传递到命令的参数位。这时候就需要xargsxargs实现的是将管道传递过来的stdin进行处理然后传递到命令的参数位置上。比如ls命令就不支持管道符


# 用户查找文件
[root@hpc-login3 sbin]# find /var/ -maxdepth 1 -perm /700
/var/
/var/tmp
/var/lib
/var/run
/var/lock
/var/log
/var/adm

#用户希望处理查找后的文件
[root@hpc-login3 sbin]# find /var/ -maxdepth 1 -perm /700|ls
abrt-auto-reporting      corosync                     fence_bladecenter          ibstat                  lspci                  opaenableports              quot                       sulogin
abrt-configuration       corosync-cfgtool             fence_brocade              ibstatus                luseradd               opaesmanalysis              quotacheck                 sushell

[root@hpc-login3 sbin]# find /var/ -maxdepth 1 -perm /700|xargs ls
/var/.updated

/var/:
account  adm  cache  crash  db	empty  ftp  games  gopher  kerberos  lib  local  lock  log  mail  named  net-snmp  nis	opt  preserve  run  spool  target  tmp	www  yp

/var/account:
pacct

/var/adm:

/var/cache:
abrt-di  cups  fontconfig  gdm	httpd  ibus  ibutils  krb5rcache  ldconfig  libvirt  man  PackageKit  realmd  tomcat  yum

##find /var/ -maxdepth 1 -perm /700|ls命令相当于处理了两个命令,一个是find命令,一个是在当前目录/sbin下执行了ls命令

使用xargs后就将find命令的stdout作为参数传递给了ls,也就是xargs完成了两个行为:

  1. 处理管道传输过来的stdin
  2. 将处理后的数据传递到正确的位置;

1.2 xargs对数据的处理

处理大量数据的时候,可能会发生参数列表过长的情况。我们知道xargs可以完成参数的定位,那xrags如何处理管道传输的stdin

其实就是完成两个操作:
1. 对数据的分割;
2. 对数据的分批;

xargs处理的顺序:先分割,在分批,然后传递到参数位

比如我们需要对一堆数据进行处理,实际上是对一堆中的每个数据进行分别的处理。那么如何将一堆数据按照自定义规则分割为独立的数据?若是一次性传递的数据过多,又该如何处理?

1.2.1 xrags的并发处理

注意:默认情况,虽然xrags实现了对数据的分批,数据分批传递后还是一次执行一个,而且有些时候分批之后是将其作为一个参数的整体,并不会将分批中的信息分段执行,因此并没有提高任何效率

为了提高处理效率,xargs提供-P选项,用于指定并发执行的数量(默认是只要一个处理进程,不会提供效率,但是可以指定为N个子进程,或者指定为0表示尽可能多的利用CPU)。这样就能将让分批操作更好的利用多核CPU,从而提升效率。例如上面分成了两批,指定-P 2可以并发执行两批,而并非执行完第一批之后再执行第二批

2、xrags命令处理数据细节

xrags对于数据的处理细节如下:

2.1 测试数据准备


[root@vms11 tom]# mkdir tmp
[root@vms11 tom]# cd tmp
[root@vms11 tmp]# mkdir a b c d test logdir shdir
[root@vms11 tmp]# touch "one space.log"
[root@vms11 tmp]# touch logdir/{1..10}.log
[root@vms11 tmp]# touch shdir/{1..5}.sh
[root@vms11 tmp]# echo "the second sh the second line" > shdir/2.sh
[root@vms11 tmp]# cat <<eof>shdir/1.sh
> the first sh
> the second line
> eof

2.2 linux中“空白”解释

xrags在linux中一般都是根据“空白”(比如: 空格" ",制表符"\t",换行“\n”),进行分隔与分批,下面介绍linux中“空白”的含义。

如上图所示:

  • 正方形代表:(文本意义)空格" ";
  • 椭圆形代表:(文本意义)换行符`"\n"``
  • 圆形代表:(标记意义)制表符"\t"
  • 长方形代表:(标记意义)换行符"\n"

2.3 xrags的分割行为

xrags分割有两种方式:

  • xrags不带-d、-0参数的默认分割,用空格进行数据分段;
  • 指定特殊符号进行分割,xrags -d "特殊符号"xrags -0的数据分割,使用特殊符号进行数据分段;
    • xargs -d可以指定分割符,可以是单个符号、字母或者数字。如指定o为分割符:xargs -d "o"
    • xargs -d是分割阶段的选项,所以会优先于分批选项(-n、-L、-i)
    • xargs -d不是先xargs-d处理的,它是区别于独立的xargs的另外一个分割选项
    • xrags -0的数据分割,使用"\0"也就是null字符进行数据分段,原理与xrags -d "特殊字符"基本一致,只是-d是指定分割符,-0是指定固定的\0作为分割符。等价于xargs -d "\0"

xrags数据分割流程如下

2.3.1 分隔前的空白转化

xrags在处理分割前,会对数据中的空白进行转化,转化后再按分隔规则,对数据进行分段。转化分为两种,一种是xrags的默认装换方式(不指定特定分割符)。一种是指定特定分割符号(xargs -dxargs -0)后的转换方式。

2.3.1.1 xrags默认的空白转化

单独的xargs命令会将接收的stdin所有的 空白(空格、制表符、换行符) 都转换为 文本意义的空格

[root@vms11 tmp]# cat shdir/1.sh
the first sh
the second line
[root@vms11 tmp]# cat shdir/1.sh | xargs echo #xargs将文本意义的换行符转化为了文本意义的空格
the first sh the second line

示例:xargs将标记意义的换行符转化为了文本意义的空格

示例:xargs将标记意义的换行符“\n”转化为了文本意义的空格

2.3.1.2 xrags指定特殊分割符的空白转化

xargs -dxargs -0将接收stdin所有的标记意义的符号替换为\n,替换完成后所有的符号(空格、制表符、分行符)变成文本意义 上的普通符号。

2.3.2 转换分段空格并分隔文件

2.3.2.1 xargs默认情况

因为xargs默认情况下将空格作为分割符,因此xargs默认情况下会将所有”空白转换成的空格“以及原有的文本意义空格都转换为“分段空格”,然后根据“分段空格”对数据进行分割。

2.3.2.2 xargs指定特殊分割符

将接收stdin所有的【标记意义】的符号替换为\n,替换完成后所有的符号(空格、制表符、分行符)变成【文本意义】上的普通符号。然后将“制定特殊分割符”替换为“分段空格”,然后根据“分段空格”对数据进行分割。因此,分段中会包含文本意义上的空格、制表符、分行符

2.3.3 示例演示:

1、使用xargs -d命令将"o"作为分割符,对ls命令的输出就行分割、分批、显示

命令:ls | xargs -d "o"

由上图可见,xargs将原来stdin发送过来得数据中的"\t"空白转换成了"\n",同时将“o”转换为了分段空格。然后将数据分割成了4个部分。如下所示:

命令:

[root@vms11 tmp]# ls |xargs -d "o" -n1 -t
# 使用"o"作为特殊分割符对标准输入数据分割成数据段,然后分批每批1个段(-n1),每次输出之前都打印命令(-t)
echo a
b
c
d
l
a
b
c
d
l
echo gdir

gdir

echo ne space.l
ne space.l
echo g
shdir
test

g
shdir
test

2、使用xargs -0将"\null"作为特殊字符,并分批数据每批2个段

[root@vms11 tmp]# ls
a  b  c  d  logdir  one space.log  shdir  test
[root@vms11 tmp]# ls | tr "\n" "\0" |xargs -0 -n2 -t
# 将ls命令查询出来每个字符串后的"\n"替换成"\0",然后使用xargs -0,将"\0"作为特殊分割符,并替换为分段空格,将数据流分段,然后分批每批两个段。
echo a b
a b
echo c d
c d
echo logdir one space.log
logdir one space.log
echo shdir test
shdir test

备注:其实ls命令查询出来的文件,每一个后面都是"\n",但是并没有换行显示

2.3.4 实际应用-使用find+xargs -0删除文件名带有空格的文件

# 创建两个文件名带空格的文件用于测试
[root@vms11 tmp]# touch "one file.txt" "two file.txt"
[root@vms11 tmp]# ls
a  b  c  d  logdir  one file.txt  one space.log  shdir  test  two file.txt

#使用find可以查找到这两个文件
[root@vms11 tmp]# find ./ -name "*.txt"
./one file.txt
./two file.txt

#因为xargs默认情况下是采用空格作为分隔符,因此会把one file.txt,two file.txt,分隔为./one,file.txt,./two,file.txt这样四个文件,这样就会造成报错,无法删除。
[root@vms11 tmp]# find ./ -name "*.txt" |xargs rm
rm: 无法删除"./one": 没有那个文件或目录
rm: 无法删除"file.txt": 没有那个文件或目录
rm: 无法删除"./two": 没有那个文件或目录
rm: 无法删除"file.txt": 没有那个文件或目录
#其实使用find ./ -name "*.txt" -exec rm -rf {} \;也能完成删除,但是如果有大量这类文件时,应为-exec没有数据分批处理以及多线程处理功能,会导致执行很慢或系统卡死。


#find命令查询出来的每个字符串结尾都是"\n",因此可以将“\n”替换为"\0",然后通过xargs -0,使用"\0"作为分割符分段,作为参数传送给rm,这样就会传送完整的文件名,就可以执行删除了
[root@vms11 tmp]# find ./ -name "*.txt" | tr "\n" "\0"|xargs -0 rm
[root@vms11 tmp]# ls -al
总用量 0
drwxr-xr-x  9 root root 102 11月 10 17:46 .
drwx------. 9 tom  tom  136 11月 10 10:18 ..
drwxr-xr-x  2 root root   6 11月 10 10:18 a
drwxr-xr-x  2 root root   6 11月 10 10:18 b
drwxr-xr-x  2 root root   6 11月 10 10:18 c
drwxr-xr-x  2 root root   6 11月 10 10:18 d
drwxr-xr-x  2 root root 137 11月 10 10:18 logdir
-rw-r--r--  1 root root   0 11月 10 10:18 one space.log
drwxr-xr-x  2 root root  66 11月 10 10:18 shdir
drwxr-xr-x  2 root root   6 11月 10 10:18 test

2.4 xargs分批行为

对于xargs,不写命令时默认的执行是echo

[root@vms11 tmp]# cat shdir/1.sh |xargs
the first sh the second line
[root@vms11 tmp]# cat shdir/1.sh |xargs echo
the first sh the second line
[root@vms11 tmp]# cat shdir/1.sh
the first sh
the second line

这里xargs将cat命令输出数据中的"\n"给替换为了空格。在xargs进行数据传输时,可以将stdin处理(分割)后作为一个整体传递给后面的命令,也可以分批后作为参数进行传递。

2.4.1 xargs分批过程

2.4.2 xargs分批参数说明

xargs命令使用-n和-L两个参数来进行分批。(还有-i参数虽然是多位置参数传递参数,但是也起到分批的作用)

用法:xargs [选项] -n或-L num

前面的选项可有可无,表示将分割后的num个段组成一批数据,作为参数传递给后面命令

2.4.2.1 -n与-L参数的区别

xargs -dxargs -0后使用-n,-L参数时,二者没有区别。
但是在单独的xargs后使用时,xargs -n会继续将空格作为默认的分割字符,替换成分段空格,然后分段分批。但是xargs -L会将"\n"作为默认的分割字符,替换成分段空格,然后分段分批。如下所示:

由上图可见,使用xargs -d “o”时,因为指定了o作为特殊分割符,所以分割的段都一致,所以-n和-L分批后的数据也都一致。

但是单独使用xargs -nxargs -L时,产生了差异:

  • xargs -n将空格作为默认分割符,所以分割时将“one space.log”这个文件名,分为了两个段“one”和“space.log”。
  • xargs -L将"\n"作为默认分割符,所以分割时将“one space.log”这个文件名,还是分割为一个段。
    因此,两者的分批出现了差异。

【需要注意的是】:

  • -i参数作为“多位置参数传递”的参数,本身也具有分批的作用,而且只能分为一个段一批。
  • -i参数与xargs一起单独使用时,会默认将"\n"作为分割符,进行分割分批。
  • -i参数与-n、-L参数不能同时生效,如果同时使用的生效规则是:谁指定在后面,谁就生效
[root@vms11 tmp]# ls |xargs -d "o" -i touch {}.testtxt
[root@vms11 tmp]# ls -al
总用量 0
drwxr-xr-x  9 root root 203 11月 10 19:21 .
drwx------. 9 tom  tom  136 11月 10 10:18 ..
drwxr-xr-x  2 root root   6 11月 10 10:18 a
-rw-r--r--  1 root root   0 11月 10 19:21 a?b?c?d?l.testtxt
drwxr-xr-x  2 root root   6 11月 10 10:18 b
drwxr-xr-x  2 root root   6 11月 10 10:18 c
drwxr-xr-x  2 root root   6 11月 10 10:18 d
-rw-r--r--  1 root root   0 11月 10 19:21 gdir?.testtxt
-rw-r--r--  1 root root   0 11月 10 19:21 g?shdir?test?.testtxt
drwxr-xr-x  2 root root 137 11月 10 10:18 logdir
-rw-r--r--  1 root root   0 11月 10 19:21 ne space.l.testtxt
-rw-r--r--  1 root root   0 11月 10 10:18 one space.log
drwxr-xr-x  2 root root  66 11月 10 18:53 shdir
drwxr-xr-x  2 root root   6 11月 10 10:18 test
# 可以看到-i与-d参数使用时,还是使用-d指定的"o"作为分割符分割了段,然后-i按一个段一批,将参数传递给了touch,创建了相应的文件,这里的"?"是"\n".


[root@vms11 tmp]# ls |xargs -i touch {}.testtxt
[root@vms11 tmp]# ls -la
总用量 0
drwxr-xr-x  9 root root 262 11月 10 19:36 .
drwx------. 9 tom  tom  136 11月 10 10:18 ..
drwxr-xr-x  2 root root   6 11月 10 10:18 a
-rw-r--r--  1 root root   0 11月 10 19:36 a.testtxt
drwxr-xr-x  2 root root   6 11月 10 10:18 b
-rw-r--r--  1 root root   0 11月 10 19:36 b.testtxt
drwxr-xr-x  2 root root   6 11月 10 10:18 c
-rw-r--r--  1 root root   0 11月 10 19:36 c.testtxt
drwxr-xr-x  2 root root   6 11月 10 10:18 d
-rw-r--r--  1 root root   0 11月 10 19:36 d.testtxt
drwxr-xr-x  2 root root 137 11月 10 10:18 logdir
-rw-r--r--  1 root root   0 11月 10 19:36 logdir.testtxt
-rw-r--r--  1 root root   0 11月 10 10:18 one space.log
-rw-r--r--  1 root root   0 11月 10 19:36 one space.log.testtxt
drwxr-xr-x  2 root root  66 11月 10 18:53 shdir
-rw-r--r--  1 root root   0 11月 10 19:36 shdir.testtxt
drwxr-xr-x  2 root root   6 11月 10 10:18 test
-rw-r--r--  1 root root   0 11月 10 19:36 test.testtxt
# xargs -i单独使用时,还是默认使用"\n"作为分割符分割了段,然后-i按一个段一批,将参数传递给了touch,创建了相应的文件.

2.5 xargs传递参数

xargs默认会在将分批后的数据传递到其后命令的最后位置。
但是当遇到需要再多个位置传递参数或者需要传递参数的位置不在中间位置(如需要传参的位置在中间位置echo “abc 参数 def”)的时候,就需要-i或者-I参数来完成,例如:重命名备份的时候在每个传递过来的文件名加上后缀.bak,这需要两个参数位。

2.5.1 语法

使用xargs -i时以大括号{}作为替换符号,传递的时候看到{}就将结果替换,可以将{}放在任意需要传递的参数位置上。如果多个地方使用{}就实现了多个传递。

xargs -Ixargs -i是一样的,只是-i默认使用大括号作为替换符号,-I可以指定其他符号、字母、数字作为替换符号,但是必须用引号包起来。man推荐使用-I代替-i,但是一般使用-i方便,除非在命令中不能使用{},例如touch {1...10}.log时,大括号就不能用来做替换符号。

2.5.2 示例

1、使用xargs命令实现文件重命名备份

[root@vms11 tmp]# ls logdir
10.log  1.log  2.log  3.log  4.log  5.log  6.log  7.log  8.log  9.log
[root@vms11 tmp]# ls logdir|xargs -i mv ./logdir/{} ./logdir/{}.bak
[root@vms11 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完成了{}位置的参数传递

2、想将数字1-10每三个数显示在startend之间

# 创建测试文件
[root@vms11 tmp]# cat logdir/10.log.bak
1
2
3
4
5
6
7
8
9
10

# [root@vms11 tmp]# cat logdir/10.log.bak|tr "\n" "\0"|xargs -0 -n 3|xargs -i echo "start {} end"
start 1 2 3 end
start 4 5 6 end
start 7 8 9 end
start 10 end

#因为每行都是以"\n"结尾的,使用"\0"进行替换,替换以后使用xargs -0进行分割,分割后的段使用-n参数每3个作为一批分批,但是因为在一个xargs中-n与-i不能同时,因此将分批生成的stdin数据在传送给下一个xargs -i,这时候stdin传送的数据已经成为
1 2 3
4 5 6
7 8 9
10
xargs -i将"\\n"作为分割符,进行分割然后每批一个段分批,然后传递到{}参数位置,完成处理。

2.6 xargs观测命令执行过程的选项

使用-p选项是交互询问式的,只有每次询问的时候输入y(或者yes)才会执行,直接按entry键是不会执行的。

使用-t选项是在每次执行xargs后面的命令都会先在stderr上打印一遍命令的执行过程然后才正式执行。

使用-p-t选项就可以根据xargs后面的命令的执行顺序进行推测,xargs是如何分段,分批以及传递的。

[root@vms11 tmp]# cat logdir/10.log.bak|tr "\n" "\0"|xargs -0 -n 3|xargs -i -t -p echo "start {} end"
echo start 1 2 3 end ?...y
echo start 4 5 6 end ?...start 1 2 3 end
y
start 4 5 6 end
echo start 7 8 9 end ?...y
echo start 10 end ?...start 7 8 9 end
y
start 10 end

3、xargs选项的典型应用

3.1 同一目录下文件过多,需要删除

分批选项有时特别有用,例如脚本规定每次只能传递三个参数,有时候grep或者rm -rf文件数量特别多的时候会提示参数列表过长而导致失败,这时候就可以分批来按批查询或删除。

命令:`ls | xargs -n 10000 rm -rf

3.2 find+xargs使用

find命令将匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分而不是全部。不像-exec选项那样,这样就可以先处理最先获取的一部分文件,然后是下一批

3.2.1 find+xargs删除大量文件名带有空格的文件

[root@vms11 tmp]# touch ./test/{1..10000}" file".txt
[root@vms11 tmp]# ls ./test/
10000 file.txt  1900 file.txt  2800 file.txt  3700 file.txt  4600 file.txt  5500 file.txt  6400 file.txt  7300 file.txt  8200 file.txt  9100 file.txt
1000 file.txt   1901 file.txt  2801 file.txt  3701 file.txt  4601 file.txt  5501 file.txt  6401 file.txt  7301 file.txt  8201 file.txt  9101 file.txt ....
[root@vms11 tmp]# find ./test/ -iname "*.txt"|tr "\n" "\0"|xargs -0 -n 10000 rm
[root@vms11 tmp]# ls ./test/

3.2.2 find+xargs实现跨目录的grep查找

ls+grep跨目录查询时,我们将文件通过管道输入到grep参数处,此时并没有包含目录地址,边会出现下面的错误。

[root@vms11 tmp]# pwd
/home/tom/tmp
[root@vms11 tmp]# ls
a  b  c  d  logdir  one space.log  shdir  test
[root@vms11 tmp]# cd a
[root@vms11 a]# pwd
/home/tom/tmp/a
[root@vms11 a]# ls ../shdir/ | xargs -n 3 grep first {}
grep: {}: 没有那个文件或目录
grep: 1.sh: 没有那个文件或目录
grep: 2.sh: 没有那个文件或目录
grep: 3.sh: 没有那个文件或目录
grep: {}: 没有那个文件或目录
grep: 4.sh: 没有那个文件或目录
grep: 5.sh: 没有那个文件或目录

find命令将匹配到文件时输出的是带路径的文件名,再传递给xargs命令执行grep就可以完成跨目录grep


[root@vms11 a]# find  ../shdir/ -iname *.sh | xargs -n 3 grep first
../shdir/1.sh:the first sh

3.3 指定并发进程-P(提高效率)

seq 5 | xargs -I{} -P5 curl -X GET "http://localhost:8090/api/gen?nickname=白晶晶&sex=她&style=二哈&check=1"

-P5 让 xargs 并发执行 5 个 curl 请求,可以大幅加快任务执行速度。

4、 xargs常用选项说明

  • -I {}: 自定义占位符{},用于替换输入
  • -n N: 对于分割的数据,每次传入N个作为一批
  • -P N: 开启N个并发任务
  • -0 : 将’\0‘字符作为分割符
  • -d : 指定分割符
posted on 2025-05-28 09:22  平复心态  阅读(209)  评论(0)    收藏  举报