xargs 工作中常用的场景

xargs拥有各种骚操作,本文仅仅例举一些比较关键的,工作中常用的,在执行单行命令的时候xargs看着简洁,但也不可避免的造成了阅读困难(尤其是不太熟悉xargs的话),如果是书写脚本,建议不要使用xargs,正常用循环,维护更方便

横向文本 纵向显示

这种操作某些场景下可以方便我们过滤文本内容

命令
==========
echo "1 2 3 4 5 6 7 8 9 10" | xargs -n 1 echo


输出演示
==========
[root@192_168_31_106 ~]# echo "1 2 3 4 5 6 7 8 9 10" | xargs -n 1 echo
1
2
3
4
5
6
7
8
9
10


相关解释
==========
echo "1 2 3" | xargs -n 1 echo 
# 上面这条命令等价于下面这样
echo 1
echo 2
echo 3
# 当然我们也可以利用循环实现此需求
for i in 1 2 3 4 5 6 7 8 9 10; do echo $i; done


扩展理解
==========
[root@192_168_31_106 ~]# echo "1 2 3 4 5 6 7 8 9 10" | xargs -n 2 echo
1 2
3 4
5 6
7 8
9 10
#可以看到,这里就相当于 "每次echo 2个"

[root@192_168_31_106 ~]# echo "1 2 3 4 5 6 7 8 9 10" | xargs -n 3 echo
1 2 3
4 5 6
7 8 9
10
#可以看到,这里就相当于 "每次echo 3个"

[root@192_168_31_106 ~]# echo "1 2 3 4 5 6 7 8 9 10" | xargs echo
1 2 3 4 5 6 7 8 9 10
#可以看到,这里就相当于 "每次echo 全部",跟直接echo输出的信息内容一致了

这里的echo是可以省略的,xargs后面默认跟的就是echo命令

纵向文本 横向显示

这里纯粹让我们,更充分理解 xargs 的工作机制

命令
==========
seq 10 | xargs echo


输出演示
==========
[root@192_168_31_106 ~]# seq 5
1
2
3
4
5
[root@192_168_31_106 ~]# seq 5 | xargs echo
1 2 3 4 5
[root@192_168_31_106 ~]# seq 10 | xargs -n 5 echo
1 2 3 4 5
6 7 8 9 10

有了上面的铺垫,下面我们列举一些更实际的、工作中能用到的场景,需要指出的是基本上xargs能实现的场景,写for循环都能实现,只不过书写要麻烦写,基本要用循环分多行写

这里的echo是可以省略的,xargs后面默认跟的就是echo命令

批量kill进程

ps -ef | grep ping | grep -v grep | awk '{print $2}' | xargs -n 1 kill
#上面这条命令相当于 
kill pid1
kill pid2

ps -ef | grep ping | grep -v grep | awk '{print $2}' | xargs kill
#上面这条命令相当于
kill pid1 pid2

可以看到这两种写法等价,因为对于kill命令来说,这两种写法等价

配合find使用

1 将/data目录下的修改时间是7天以前,并且大于100k的文件复制到到/tmp目录下
===================
find /data/ -mtime +7 -type f -size +100k | xargs cp -t /tmp/
这条命令就有点意思了,首先我们直到find命令返回的是垂直排列的文件名列表,而使用cp拷贝文件,常用的方法是下面这样
cp file1 file2 file3 /tmp
可以看到,需要拷贝的文件在中间,而xargs需要的参数在末尾,因此我们利用cp命令的 -t 参数


2 查找出系统中大于50k且小于100k的文件,把文件中的的hello替换为为world
===================
find / -type f -size +50k -size -100k | xargs sed -i 's#hello#world#g'

-I 参数 临时替换

#前面提到过的下面这个用法,如果大家觉得理解不方便
find /data/ -mtime +7 -type f -size +100k | xargs cp -t /tmp/
#我们还可以这样写
find /data/ -mtime +7 -type f -size +100k | xargs -I mytmp cp mytmp /tmp/
#可以看到,这里我们用mytmp来代指前面xargs找到的内容,这里的mytmp可以任意起一个名称

大批量删除文件

find /data/ -mtime +7 -type f -size +100k | xargs rm -rf
上面的情况不用xargs可以写喜循环实现,还可以下面这样
rm -rf `find /data/ -mtime +7 -type f -size +100k`

如果删除的文件太多,比如几十万、上百万不建议使用xargs,性能不好,曾经我在工作中使用xargs删除20多万个Kafka Topic,删了一夜,因此我们老老实实使用循环删除即可,我一般都用类似下面这样

for i in `find /data/ -mtime +7 -type f -size +100k`; do
echo "rm -rf $i" >> /tmp/tmp.sh
done

不过这种删除方法我们还是要了解下,避免有人在我们面前炫技,抑或是别人脚本里出现这种用法,我们能够直到这里是干什么的就行

处理的文件名包含空格

如果处理的文件中有空格,等一些特殊字符(文件名最好不要包含特殊字符),需要一个额外的参数,如下示例

touch hello-world
touch "hello world1"
touch "hello world2"

[root@192_168_31_106 ~/xtmp]# ls | xargs ls
ls: cannot access hello: No such file or directory
ls: cannot access world1: No such file or directory
ls: cannot access hello: No such file or directory
ls: cannot access world2: No such file or directory
hello-world

可以看到,由于"hello world1"被拆分成了两个文件,所以报错文件找不到,我们可以通过 --null (或 -0)参数解决次问题
find . -type f  -print0 | xargs --null -n 1 ls

find -print0 与 xargs --null 经常用来配合使用,处理文件名带有空格的问题 文件名包含空格的坑很多,我们首先要仔细梳理下业务场景,为啥会导致文件名会包含空格,尽可能从源头解决问题,杜绝文件名包含空格的存在

打印出要执行的命令

# -t OR --verbose 参数
[root@192_168_31_106 ~/xtmp]# echo 'one two three' | xargs -n 1 -t  touch
touch one 
touch two 
touch three 
[root@192_168_31_106 ~/xtmp]# ls
one  three  two

另一个测试
ls | xargs -t -I mytmp cp -a mytmp /tmp/

在执行命令前给出确认提示

[root@192_168_31_106 ~/xtmp]# echo 'hello1 hello2 hello3' | xargs -n 1 -p  touch
touch hello1 ?...y
touch hello2 ?...y
touch hello3 ?...y
[root@192_168_31_106 ~/xtmp]# ls
hello1  hello2  hello3

可以输入 y/Y/yes 来表示确认执行

参考资料

http://www.ruanyifeng.com/blog/2019/08/xargs-tutorial.html