xargs--冬天里的一丝暖意

本文为博客园作者所写: 一寸HUI,个人博客地址:https://www.cnblogs.com/zsql/

你有批量kill作业吗?有因为删除文件夹的内容太多而报错吗?-bash: /bin/rm: Argument list too long,查找出来的文件怎么直接删除呢?批量执行?并发执行?如果有这样的需求的话,就可以开始学习xargs命令了,本文主要的思路先提供一些简单的使用场景,然后再对xargs命令原理和使用进行阐述。

一、简单使用场景

1.1、使用场景

  • 传递参数,从而组合多个命令
  • 分隔数据,避免参数过长
  • 不支持管道的命令

1.2、具体实例

1.1、批量删除hive用户的所有进程

ps aux | grep ^hive | grep -v grep | awk '{print $2}' | xargs kill -9

1.2、分批次删除logs目录下2020年的所有文件,每批为10个

cd  /logs && ls  | grep *2020* | xargs -n 10 rm -fr
ls /logs | sed "s:^:`pwd`/:" | grep *2020* | xargs -n 10 rm -fr

1.3、结合find命令进行处理文档,find命令可以查看:linux之find命令详解

find . -name *.log -print0 | xargs rm -f #查找当前目录下的所有log文件,并删除
find ./ -type f -name "*.txt" | xargs -i cp {}  /tmp/ #查找txt文件,并复制到/tmp目录下
find ./ -type f  -name "*.log" | xargs -i -t  tar -zcvf {}.tar.gz {} #压缩日志文件

1.4、批量下载

cat urls.txt | xargs wget

1.5、如果打通了ssh免密,假如有ip:192.168.88.130,192.168.88.131

echo "192.168.88.130,192.168.88.131" | xargs -n 1 -P 2 -I __ip__ scp root@__ip__:/var/log/cron ./__ip__.cron #把这两个机器的cron日志复制到本地,取名为${ip}.cron

二、原理和使用

2.1、使用语法

[root@lgh~]# xargs --help
Usage: xargs [-0prtx] [--interactive] [--null] [-d|--delimiter=delim]
       [-E eof-str] [-e[eof-str]]  [--eof[=eof-str]]
       [-L max-lines] [-l[max-lines]] [--max-lines[=max-lines]]
       [-I replace-str] [-i[replace-str]] [--replace[=replace-str]]
       [-n max-args] [--max-args=max-args]
       [-s max-chars] [--max-chars=max-chars]
       [-P max-procs]  [--max-procs=max-procs] [--show-limits]
       [--verbose] [--exit] [--no-run-if-empty] [--arg-file=file]
       [--version] [--help] [command [initial-arguments]]

2.2、原理说明

  在1.1中我们说了xargs使用的三个场景,一个是参数太长导致命令不能执行,就像rm一样,如果rm -rf /log/*,正如log目录下有很多很多个文件,这样就会出现/bin/rm: Argument list too long这样的错误,那为啥使用xargs就能删除了呢,因为xargs默认会把参数截取分批进行执行,这里使用了xargs的分割数据和传递参数原理,还有就是有些命令并不支持管道符,像ls命令,但是又想使用一行命令搞定,这样就可以使用xargs配合使用了。比如使用命令:echo text.txt  | xargs cat,这样可以把text.txt传递给cat命令作为参数,这样就可以查看该文件了,也就是说xargs完成了两个行为:处理管道传输过来的stdin;将处理后的传递到正确的位置上,还可以将stdin或者文件stdin分割成批,每个批中有很多分割片段,然后将这些片段按批交给xargs后面的命令进行处理,可能看到这里会有点懵逼,那简单点就是:xargs处理的优先级或顺序了:先分割,再分批,然后传递到参数位。

  举个例子:假如你有一个蛋糕,你要分给10个孩子去吃,首先第一步要进行切蛋糕(分割)那怎么切呢,切10块还是20块或者其他呢?所以这里就会存在切蛋糕方式的选择(分割方式),切好蛋糕后,小孩子都在不同的地方,你是选择每次送一块蛋糕呢,还是一次送两块呢(这就是分批,每批次传送多少个),送也要找到小孩的位置才能送到他手上吧,(这就是把分好批的文件或数据传递到命令指定的参数的位置),我想大家应该比较容易理解这个了,如果你还是没能理解,多看几遍:xargs处理的优先级或顺序了:先分割,再分批,然后传递到参数位,然后继续看下面的参数例子就懂了

2.3、参数详解

2.3.1、分割参数

分割有三种方法:独立的xargs、xargs -d和xargs -0,后两者可以配合起来使用,既然都可以用来分割,那我们看看分割的方式:

1、xargs分割:将接收到的stdout处理后传递到xargs后面的命令参数位,不写命令时默认的命令是echo,会对空格、tab、换行符,反斜线等进行分割

[root@lgh test]# echo -e "a b c \n dd \t y"
a b c
 dd      y
[root@lgh test]# echo -e "a b c \n dd \t y" | xargs #默认会使用echo命令输出,这里的换行符和空格以及制表符都被作为分隔符用来分割了
a b c dd y
[root@lgh test]# echo -e "a b c \n dd \t y" | xargs echo
a b c dd y

将所有空格、制表符和分行符都替换为空格并压缩到一行上显示,这一整行将作为一个整体,这个整体的所有空格属性继承xargs处理前的符号属性(大概理解为,把这些分割好后用自己的所要的“段”替代,干掉别人的心腹(分隔符),换成自己的心腹)

2、xargs -d分割:可以指定分段符,可以是单个符号、字母或数字。如指定字母o为分隔符:xargs -d"o"

[root@lgh test]# echo -e "a bd\n cod \n dod \t y"
a bd
 cod
 dod     y
[root@lgh test]# echo -e "a bd\n cod \n dod \t y" |xargs -d 'o' #使用o进行分割,但是这样可能看的不是很清楚
a bd
 c d
 d d     y

这样看可能就清楚了:

[root@lgh test]# echo -e "a bd\n cod \n dod \t y" |xargs -p -n 1  -d "o"  #-p参数交互参数,-n 1执行每批的个数,这些后面会说到
/bin/echo a bd
 c ?... #打印第一批
/bin/echo d
 d ?... #打印第二批
/bin/echo d      y
 ?... #打印第三批
/bin/echo ?...

从上就可以看出来是指定的字符"o"进行分割了,使用xargs -d分割,会忽略空格、制表符、分行符等这些符号,会把这些当做内容保留下来,如上面的换行符都有保留下来,只有指定的"o"才是分割符

3、xargs -0分割:可以处理接收的stdin中的null字符(\0),其实可以理解是xargs -d的一种特列,等价于xargs -d "\0"

[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd"
1 bbb
uudddd
[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd" | xargs -0
1 bbb
uu dddd

[root@mvxl2685 test]# echo -e "1\tbbb \n uu\0dddd" | xargs -0 -p -n 1
/bin/echo 1 bbb
uu ?... #第一批
/bin/echo dddd
?... #第二批
/bin/echo ?...

上面三种分割方式,使用最多的也就是默认的xargs了

分割行为

特殊符号处理方式

分段方法

配合分批选项

分批方法

xargs

空格、制表符、分行符替换为空格,引号和反斜线删除。处理完后只有空格。如果空格、制表符和分行符使用引号包围则可以保留

结果继承处理前的符号性质(文本符号还是标记意义符号)。

-n

以分段结果中的每个空格分段,进而分批。不管是文本还是标记意义的空格,只要是空格

-L、-i

以标记意义上的空格分段,进而分批

不指定

结果作为整体输出

xargs -d

xargs -d   不处理文本意义上的符号,所有标记意义上的符号替换为换行符\n,将-d指定的分割符替换为标记意义上的空格。

结果中除了最后的空行和-d指定的分割符位的分段空格,其余全是文本意义上的符号

按照-d指定的符号进行分段,每个段中可能包含文本意义上的空格、制表符、甚至是分行符。

-n、-L、-i

以标记意义上的符号(即最后的空行和-d指定分隔符位的空格)分段,进而分批。分段结果中保留所有段中的符号,包括制表符和分行符。

不指定

结果作为整体输出

xargs -0

不处理文本意义上的符号,将非\0的标记意义上的符号替换为\n,将\0替换为空格。

结果中除了最后空行和\0位的空格,其余都是文本意义上的符号

以替换\0位的空格分段,每个段中可能包含文本意义上的空格、制表符、甚至是分行符。

如果没检测到\0,则只有一个不可分割的段。

-n、-L、-i

检测到\0时,以标记意义上的符号(即最后的空行和\0位的空格)分段,进而分批。分段结果中保留所有段中的符号,包括制表符和分行符。

未检测到\0时,整个结果作为不可分割整体,使用分批选项是无意义的

不指定

结果作为整体输出

2.3.2、分批参数

上面一节中我们知道怎么切蛋糕了(分割),接下来我们就怎么把这些分割好的内容组装成批次。可以使用-n和-L两个参数,那具体他们有什么区别呢?请继续往下看:
1、使用-n分批

假如有文件夹test:

[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:33 a
-rw-r--r-- 1 root root 0 Dec  4 14:33 b
-rw-r--r-- 1 root root 0 Dec  4 14:33 c
-rw-r--r-- 1 root root 0 Dec  4 14:33 dd
-rw-r--r-- 1 root root 0 Dec  4 14:33 gg
-rw-r--r-- 1 root root 0 Dec  4 14:33 u t.txt #这是个特殊文件,有空格的文件名
-rw-r--r-- 1 root root 0 Dec  4 14:33 uu

接下我们使用-n分批会有怎样的效果呢?

[root@lgh test]# ls | xargs -n 2 #每两个作为一个批次
a b
c dd
gg u  #这里把u t.txt文件拆到两个批次中了,这并不是我们想要的
t.txt uu

xargs -n分两种情况:和独立的xargs一起使用,这时按照每个空格分段划批;和xargs -d或xargs -0一起使用,这时按段分批,即不以空格、制表符和分行符分段划批

2、使用-L分批

[root@lgh test]# ls | xargs -L 2 #每两个作为一个批次
a b
c dd
gg u t.txt #这里就没有拆分u t.txt文件
uu

-n选项类似,唯一的区别是-L永远是按段划批,而-n在和独立的xargs一起使用时是按空格分段划批的

2.3.3、位置传递参数

xargs -i和xargs -I选项在逻辑上用于接收传递的分批结果,如果不使用这两个选项,则默认是将分割后处理后的结果整体传递到命令的最尾部,有时候可能需要到多个位置,或者不是最后一个位置,例如cp命令一样,所以这两个参数很重要。

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

假如有目录test:

[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log

现在需要把它们全部备份起来,编程*.log.bak文件

[root@lgh test]# ls | xargs  -i mv  {} {}.bak #这里使用-i接收,然后使用{}指定位置
[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log.bak
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log.bak

-I则可以指定其他的符号、字母、数字作为替换符号,例如xargs -I

[root@lgh test]# ls | xargs  -I bb mv  bb bb.I #这里使用-I参数指定bb为接收替代符
[root@lgh test]# ll
total 0
-rw-r--r-- 1 root root 0 Dec  4 14:51 10.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 1.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 2.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 3.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 4.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 5.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 6.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 7.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 8.log.bak.I
-rw-r--r-- 1 root root 0 Dec  4 14:51 9.log.bak.I

注意:-i、-L、-n这三个参数在一起,都会被最后一个覆盖,也就是一起使用的时候,只有最后一个生效

2.3.4、其他参数

上面已经把最重要的一些参数介绍完了,我们看看其他参数

1、-p和-t:交互参数,可以用于前期不确定执行过程,用来测试自己写的代码是否符合最终的执行命令

-p是询问是否打印,y键表示打印,回车表示不打印

[root@lgh test]# ls | xargs -n 3 -p
/bin/echo 10.log.bak.I 1.log.bak.I 2.log.bak.I ?...y #询问打印,按下y
/bin/echo 3.log.bak.I 4.log.bak.I 5.log.bak.I ?...10.log.bak.I 1.log.bak.I 2.log.bak.I #打印出上一次询问的值,然后继续询问下一个批次是否打印,按下回车,不打印输出

/bin/echo 6.log.bak.I 7.log.bak.I 8.log.bak.I ?...
/bin/echo 9.log.bak.I ?...

-t是先把执行的语句打印出来,然后把要输出的内容打印出来

[root@lgh test]# ls | xargs -n 3 -t
/bin/echo 10.log.bak.I 1.log.bak.I 2.log.bak.I #打印执行的语句
10.log.bak.I 1.log.bak.I 2.log.bak.I #打印执行语句的结果
/bin/echo 3.log.bak.I 4.log.bak.I 5.log.bak.I
3.log.bak.I 4.log.bak.I 5.log.bak.I
/bin/echo 6.log.bak.I 7.log.bak.I 8.log.bak.I
6.log.bak.I 7.log.bak.I 8.log.bak.I
/bin/echo 9.log.bak.I
9.log.bak.I

2、-P:批量执行参数(并发,一起执行)哈哈,反正就是同时执行

在执行某一个命令的时候,后面很多参数的时候,例如:rm log/* 会对log下的每一个目录进行依次删除,就算使用xargs的分批执行,还是一个一个执行的,并不会提高效率,接下来该-P参数出场了

"-P N"选项可以指定并行处理的进程数量为N。不指定"-P"时,默认为1个处理进程,也就是串行执行。指定为0时,将尽可能多地开启进程数量

[root@lgh ~]# time echo {1..3} | xargs -n 1 sleep #不使用-P,并每个批次为1,总共的执行时间为6秒

real    0m6.005s
user    0m0.001s
sys     0m0.001s
[root@lgh ~]# time echo {1..3} | xargs -n 3 sleep #不使用-P,每个批次为3,总共执行时间为6秒,分批不能提高速度

real    0m6.003s
user    0m0.000s
sys     0m0.002s
[root@lgh ~]# time echo {1..3} | xargs -n 3 -P 3 sleep #使用-P,每个批次为3,说明只有一个批次,所以一个线程需要一次执行所有的命令,所以还是6秒,没有达到并发的效果

real    0m6.003s
user    0m0.001s
sys     0m0.000s
[root@lgh ~]# time echo {1..3} | xargs -n 1 -P 3 sleep #使用-P,每个批次为1,所以可以同时执行命令,执行时间为3秒

real    0m3.003s
user    0m0.001s
sys     0m0.001s

很多时候,执行大量命令的时候可以使用-P提高效率

2.4、总结

xargs命令很强大,你值得拥有!!!如果使用的得当不仅可以提高逼格,还可以提高效率,能够做一些原本命令不能做到的事情,好好学号xargs何乐而不为呢?

xargs的总结:分割(xargs,xargs -d,xargs -0)、分批(-n,-L)、传递到指定位置(-i,-I)

 

 

参考:

https://www.cnblogs.com/f-ck-need-u/p/5925923.html#auto_id_9

 https://blog.chenyuanwai.com/xargs/

https://blog.csdn.net/lilongsy/article/details/77535687

posted @ 2020-12-04 16:36  一寸HUI  阅读(597)  评论(1编辑  收藏  举报