find命令的处理动作以及xargs命令

自己的《Linux系统管理初学者指南--基于CentOS 7.6》已于2019年10月份出版,对于这本书的质量总体上感觉还算满意,但是限于当时的知识水平,有些地方描述的仍然不是很清楚,所以在讲课的过程中也在不断进行修订。比如最近在讲到find命令的处理动作时,不少学生就提出疑问,因而我对原书中这部分内容进行了重写,希望再版时可以订正。
find命令的处理动作以及xargs命令

创新互联是一家专业提供屯留企业网站建设,专注与成都做网站、成都网站设计、H5高端网站建设、小程序制作等业务。10年已为屯留众多企业、政府机构等服务。创新互联专业网络公司优惠进行中。

处理动作

find命令不仅可以查找,还可以对找到的结果进行进一步的处理,这就要用到“处理动作”。
例如,找出/usr/bin目录中所有的符号链接文件。
[root@localhost ~]# find /usr/bin -type l
上面这条命令查找出来的结果只显示文件名,如果我们还想查看每个符号链接文件都指向了哪个源文件,就可以在find命令的最后加上处理动作“-ls”。

[root@localhost ~]# find /usr/bin -type l -ls
50346791    0 lrwxrwxrwx   1 root    root  3 2月 14  2019 /usr/bin/captoinfo -> tic

需要注意的是,这个处理动作“-ls”属于是find命令的一部分,而并非是之前所介绍的ls命令,因而无法在处理动作“-ls”之后加上ls命令的各种选项。例如,我们希望找出/etc目录中所有大小在1MB以上的文件,并人性化显示其详细信息,那么执行下面的命令就会报错。

[root@localhost ~]# find /etc -size +1M -ls -lh
find: 未知的断言“-lh”

要实现这个需求,就要借助于find命令的另外一个处理动作“-exec”。这个处理动作可以将find命令找到的结果当作文件去处理,在“-exec”的后面要跟上进一步处理所要执行的命令,另外在命令中还需要使用符号“{}”表示find命令查找到的结果,在命令的最后必须添加“ \;”表示命令结束(注意前面有个空格)。
例如,找出/etc目录中所有大小在1MB以上的文件,并人性化显示其详细信息,下面这条命令才是正确的做法。在这条命令中,ls就是之前所介绍的ls命令,我们借助于-exec将find命令找到的结果,以文件的形式传给ls命令继续处理。

[root@localhost ~]# find /etc -size +1M -exec ls -lh {} \;
-rw-------. 1 root root 3.8M 11月  3 2018 /etc/selinux/targeted/active/policy.kern

再比如,找出/tmp目录中所有后缀为“.txt”的文件并将其删除。
[root@localhost ~]# find /tmp -name "*.txt" -exec rm -f {} \;
很多同学在这里会疑惑,为什么这里用管道符“|”(将在2.9.5节介绍)无法实现上面的操作呢?比如我们做下面的测试:

[root@localhost ~]# touch /tmp/{a,b,c}.txt  #在/tmp目录中生成3个测试文件
[root@localhost ~]# find /tmp -name "*.txt" | rm -f     #利用管道结合rm删除
[root@localhost ~]# find /tmp -name "*.txt"     #测试文件仍然存在
/tmp/a.txt
/tmp/b.txt
/tmp/c.txt
[root@localhost ~]# find /tmp -name "*.txt" -exec rm -f {} \;   #利用exec结合rm删除
[root@localhost ~]# find /tmp -name "*.txt" #测试文件被成功删除

处理动作-exec最主要的作用就是可以将find命令找到的结果当成文件去处理,而默认情况下,find命令找到的结果是被当作文本信息去处理的。
怎样理解上面这段话呢?比如对于执行“find /tmp -name "*.txt"”命令所找到的三个文件:/tmp/a.txt、/tmp/b.txt、/tmp/c.txt,默认情况下find命令只是把符合查找条件的这三个文件找到,并把它们的名字在屏幕上输出,因而我们在屏幕上所看到的只是三行文本信息。对于文本信息,可以使用之前介绍的文件内容操作命令进行处理,比如用wc命令统计行数,用grep命令进行过滤等。

[root@localhost ~]# find /tmp -name "*.txt" | wc -l     #统计find找到的文件数量
3
[root@localhost ~]# find /tmp -name "*.txt" | grep 'a'  #对find的结果进行过滤
/tmp/a.txt

对于文本信息,之前介绍的文件和目录操作命令就无法处理了,比如cp、mv、rm等,因为这些命令所操作的对象必须是文件。此时-exec就可以派上用场,因为它的主要作用就是可以将find命令找到的结果不再看作是文本信息,而是看作文件。因而如果需要对find的结果用文件操作命令进行进一步处理的话,那么就需要结合-exec。
例如,查找/boot目录下的以“init”开头的文件,并将其复制到/tmp目录。
[root@localhost ~]# find /boot -name "init*" -exec cp {} /tmp \;
除了-ls和-exec之外,还有一个比较常用的处理动作是-delete,它可以将find找到的结果直接删除。
例如之前的操作:找出/tmp目录中所有后缀为“.txt”的文件并将其删除。其实更为简便的做法是下面的命令:
[root@localhost ~]# find /tmp -name "*.txt" -delete

xargs命令

当在find命令中利用-exec对查找到的结果进行进一步处理时,有时可能会出现问题。这是因为-exec是将find所找到的结果一次性地送给后面的命令进行处理,有时候find可能会找到大量的文件,超出了后面的命令所能处理的参数范围,这时就会出现溢出错误,错误信息通常是“参数列太长”或“参数列溢出”,这时就可以使用xargs命令。xargs虽然本身是一个独立的Linux命令,但通常都是被用来配合find命令使用。通过xargs,可以将find所找到的结果分批次地送给之后的命令进行处理,从而避免出现溢出问题。
xargs命令需要通过管道与find命令配合使用,xargs的命令格式“find ……| xargs commands”。
下面我们先准备一个测试文件。

[root@localhost ~]# mkdir /tmp/pass
[root@localhost ~]# echo "password:123" >> /tmp/pass/test.txt

假设在/tmp目录中存放了大量的文件,在其中的某个文件里存放了一个密码,关键字为“password”,我们现在希望能够将这个存放了密码的文件找出来。
如果利用find命令的-exec选项,可以执行下面的命令:

[root@localhost ~]# find /tmp -type f -exec grep "password" {} \;
password:123

可以发现,虽然通过上面的命令找出了密码,但并没有显示存放该密码的文件名。下面换做用xargs命令来实现该要求,xargs就可以将关键字所在的文件一并显示出来。

[root@localhost ~]# find /tmp -type f | xargs grep "password"
/tmp/pass/test.txt:password:123

再比如,我们希望将/tmp目录以及/tmp所有下级子目录中,文件名以“.txt”作为后缀的文件都复制到/root目录中。如果用find命令的-exec来实现:
[root@localhost ~]# find /tmp -name "*.txt" -exec cp {} /root \;
如果用xargs命令来实现,同样需要用“{}”来代指find命令查找到的结果,并且需要为xargs命令添加-i选项。
[root@localhost ~]# find /tmp -name "*.txt" | xargs -i cp {} /root
通过这几个实例可以发现,xargs命令与find命令-exec处理动作的功能基本相同,所以如果-exec可以满足要求,那么就无需使用xargs命令。xargs命令的主要用途在于它可以对find命令找到的结果分批处理,避免出现溢出错误。
比如在/etc目录中一共有2507个普通文件。

[root@localhost ~]# find /etc -type f | wc -l
2507

如果我们希望能找出/etc目录中所有包含关键字“PermitRootLogin”的文件,分别用这两种方法来实现:

[root@localhost ~]# find /etc -type f -exec grep "PermitRootLogin" {} \;
#PermitRootLogin yes
# the setting of "PermitRootLogin without-password".
[root@localhost ~]# find /etc -type f | xargs grep "PermitRootLogin" 
/etc/ssh/sshd_config:#PermitRootLogin yes
/etc/ssh/sshd_config:# the setting of "PermitRootLogin without-password".

可以发现在用-exec的方法实现时,出现了明显的卡顿,如果数据量再大一些的话,可能就会导致溢出。而用xargs命令来实现,一方面更为快速,另一方面不会出现溢出问题,而且显示的内容也更为详细。所以在进行这类操作时,更加推荐使用xargs命令。


文章名称:find命令的处理动作以及xargs命令
本文路径:http://ybzwz.com/article/jesjoo.html