(019) Linux之文件搜索

十年运维系列之基础篇 - Linux

作者:曾林 

联系:1494445739@qq.com

网站:www.jplatformx.com

版权:文章未经同意请勿转载


一、引言

      Linux系统中含有非常多的文件!这就很自然地产生一个问题,那就是我们应该如何去查找文件。虽然我们知道Linux文件系统良好的组织架构,源自于类UNIX的操作系统代代传承的习俗,但是仅文件数量就会引起非常可怕的问题。

      本章我们主要介绍两个用在Linux系统中搜索文件的工具。

  • locate:通过文件名来查找文件
  • find:在文件系统目录框架中查找文件

      同时,我们还会介绍一个通常与文件搜索命令一起使用、处理搜素结果文件列表的命令。

  • xargs:从标准输入中建立、执行命令行

此外,还会介绍两个辅助工具。

  • touch:更改文件的日期时间
  • stat:显示文件或文件系统的状态

 

二、locate——较简单的方式查找文件

      locate命令通过快速搜索数据库,以寻找路径名与给定字符串相匹配的文件,同时输出所有匹配结果。例如,假定查找名称以zip字符串开头的程序,由于查找的是程序文件,所以可以认为包含所要查找的程序的目录名应以bin结尾。因此可以尝试下面的命令行:

      shell> locate bin/zip

      locate程序将搜索该路径名数据库,并输出文件名包含字符串bin/zip的所有文件。执行结果如下图所示:

      有时搜索需求并不是那么简单和纯粹,这个时候就需要使用locate命令来结合grep工具来实现一些更有趣的搜索。如下图所示:

      注:locate的搜索数据库从何而来?你也许曾注意到,有些Linux版本,系统刚刚安装好后locate命令并不能正常工作,但是如果第二天再尝试的话,就会发现它又能正确工作了,这到底是怎么回事呢?其实,是因为locate的搜索数据库由另一个叫做updatedb的程序来创建的,通常该程序作为一个cron任务定期执行。所谓cron任务就是指定期由cron守护进程执行的任务,多数装有locate命令的系统每天执行一次updatedb。由此可见,locate的搜索数据库并不是持续更新的,所以locate命令查找不到新的文件。解决方案就是切换到超级用户,然后手动执行一下updatedb程序。

 

三、find——较复杂的方式查找文件

      locate程序查找文件仅仅依据文件名,而find程序则是依据文件的各个属性在既定的目录(及其子目录)里查找。

      find最简单的用法就是用户给定一个或者更多目录名来作为搜索范围。下面就用find命令列出当前系统主目录(~)下的文件列表清单。

      shell> find ~

      find命令的美妙之处就是可以用来搜索符合特定要求的文件,它通过综合应用test选项、action选项和options选项实现高级文件的搜索。下面首先来看test选项。

1. test选项

      假定我们想要查找的是目录文件,我们可以添加下面的test选项达到此目的。

shell> find ~ -type d | wc -l

添加test参数-type d可以将搜索范围限制为目录,而下面例子中使用-type f则表示只对普通文件进行搜索。

shell> find ~ -type f | wc -l

下表中列出了find命令所支持的常用文件类型:

文件类型 描述
b 块设备文件
c 字符设备文件
d 目录
f 普通文件
l 符号链接文件

      另外我们还可以通过添加其他的test项参数实现依据文件大小和文件名的搜索。如下命令行就是用来查找所有符合*.JPG通配符格式以及大小超过1MB的普通文件。

      shell> find ~ -type f -name "*.JPG" -size +1M 

      本例中添加的-name "*.JPG"的test选项表示查找的是符合.JPG通配符格式的文件。另外添加的-size +1M的test选项,前面的+号表示查找的文件大小比给定的数值1M大。若字符串前面是减号则代表要比给定的数值小,没有符合则表示与给定值完全相等。末尾的M是计量单位MB(兆字节)的简写。下表展示的是每个字母与特定计量单位之间的对应关系。

字母 单位
b 512字节的块
c 字节
w 两个字节的字
k KB(每单位包括1024个字节)
M MB(每单位包括1048576个字节)
G GB(每单位包括1073741824字节)

      find命令支持多种test参数,下表概况了一些常见的参数。注意,前面所讲述的“+”和“-”号的用法适用于所有用到数值参数的情况。

test参数 描述
-cmin n 匹配n分钟前改变状态(内容或属性)的文件或目录。如果不到n分钟,就用-n,如果超过n分钟,就用+n
-cnewer file 匹配内容或属性的修改时间比文件file更晚的文件或目录
-ctime n 匹配系统中n*24小时前文件状态被改变(内容、属性、访问权限等)的文件或目录
-empty 匹配空文件或者空目录
-group name 匹配属于name组的文件或目录。name可以描述为组名,也可以描述为该组的ID号
-iname pattern 与-name test选项功能类似,只是不区分大小写
-inum n 匹配索引节点是n的文件。该test选项有助于查找某个特定索引节点上的所有硬连接
-mmin n 匹配n分钟前内容被修改的文件或目录
-mtime n 匹配n*24小时前只有内容被修改的文件或者目录
-name pattern 匹配有特定通配符模式的文件或目录
-newer file 匹配内容的修改时间比file文件更近的文件或者目录。这在编写shell脚本进行文件备份的时候非常有用。每次创建备份时,更新某个文件(比如日志),然后用find+此参数选项来确定上一次更新后哪个文件改变了
-nouser 匹配不属于有效用户的文件或目录。该test选项可以用来查找那些属于已删除账户的文件,也可以用来检测攻击者的活动
-nogroup 匹配不属于有效组的文件或目录
-perm mode 寻找访问权限与既定模式匹配的文件或目录。既定模式可以以八进制或符号的形式表示
-samefile name 与-inum test选项类似。匹配与file文件用相同的inode号的文件
-size n 匹配n大小的文件
-type c 匹配c类型的文件
-user name 匹配属于name用户的文件和目录。name可以描述为用户名也可以描述为该组的ID号

 

操作符

      即使拥有了find命令提供的所有test参数,我们仍然会需要一个更好的工具来描述test参数之间的关系。例如,如果我们需要确定某目录下是否所有的文件和子目录都有安全的访问权限,该怎么办?原则上就是去查找那些访问权限不是0600的文件和访问权限不是0700的子目录。幸运地是,find命令的test选项可以结合逻辑操作从而建立具有复杂逻辑关系的匹配条件。我们可以用下面的命令行来满足上述find命令的匹配搜索。

      shell> find ~ \( -type f  -not -perm 0600 \) -or \( -type d -not -perm -0700 \)

      有关find命令的逻辑操作符展示如下:

操作符 功能描述
-and(与操作) 查找使该操作符两边的检验条件都是真的匹配文件。有时直接缩写为-a。注意如果两个检测条件之间没有显示的操作符,and就是默认的逻辑关系
-or(或操作) 查找使该操作符任何一边的检测条件为真的匹配条件。有时直接缩写成-o
-not(-非操作) 查找使该操作符后面的检测条件为假的匹配条件。有时直接缩写为-!
()(括号操作) 逻辑表达式的优先权的。默认的情况下, find命令从左向右运算逻辑值。然后有时为了获得想要的结果必须扰乱默认的执行顺序,即便不需要,将一串字符表达式括起来对提高命令的可读性也很有帮助。请注意,括号在shell环境中有特殊的意义。所以必须将它们在命令行中用引号引起来,这样才能作为find的参数传递。通常用反斜杠来避免这样的问题。

      2. action选项

      行动起来吧!前面find命令已经查找到所需要的文件,但是我们真正想做的是处理这些已查找到的文件。幸运地是,find命令允许直接对搜索结果执行动作。

预定义动作

      对搜索到的文件进行操作,即可以用诸多现成的预定义动作指令,也可以使用用户自定义的动作。首先来看一下预定义动作,见下表:

动作 功能描述
-delete 删除匹配文件
-ls 对匹配文件执行ls操作,以标准格式输出其文件名以及所要求的其他信息
-print 将匹配的文件的全路径以标准形式输出。当没有指定任何具体操作的时候,该操作是默认操作。
-quit 一旦匹配成功立即退出

      知道了预定义动作之后,就不难理解上面的find ~操作的结果是打印当前主目录中所有文件和子目录的列表。该列表之所以会在屏幕上显示出来,是因为在没有指定其他操作的情况下,-print操作是默认的。因此,上述命令行等效于如下形式的命令行。

      shell> find ~ -print

用户自定义操作

      除了已有的预定义操作命令,同样也可以任意调用用户想要执行的操作命令。传统的方法就是像以下命令行使用-exec操作。

      shell> -exec command {};

      该格式中的command表示要执行的操作命令名,{}花括号代表的是当前路径,而分号作为必需的分隔符表示命令结束。使用-exec完成-delete操作示例如下:

      shell> -exec rm '{}' ';'

      同样,由于括号和分号字符在shell环境下有特殊的含义,所以在输入命令行时,要将它们用引号引起来或者转义符隔开。

      当然,交互式地执行用户自定义操作也不是不可能。通过使用-ok操作取代原来的-exec操作,每一次指定命令执行之前系统都会询问用户。有关用户自定义操作的截图如下所示:

 提高效率

    当使用-exec操作的时候,每次查找到匹配文件后都会调用执行一次指定命令。但有时用户更希望只调用一次命令就完成对所有匹配文件的操作。例如,多数人更应该喜欢这样的方式:

      shell> ls -l file1 file2

      而不是如下的方式:

      shell> ls -l file1

      shell> ls -l file2

      第一种方式只需要执行一次,而第二种方式则需要多次重复执行。实现这样的一次操作有两种方法:一种比较传统,使用外部命令xargs;另一种则是使用find命令本身自带的新特性。首先介绍第二种方法。

      通过将命令行末尾的分号改成加号,便可将find命令所搜索到的匹配结果作为指定命令的输入,从而一次完成对所有文件的操作。

      通过这样的改进就可以让系统整体只执行一次ls语句,而不是两次了。

      同样我们可以使用xargs命令来达到相同的效果,xargs处理标准输入信息并将其转化为某指定命令的输入参数列表。结合上面的例子可以得到如下的args执行结果,如下图:

      在上面的例子中,find命令的执行结果直接作为xargs命令的输入,xargs反过来将其转成了ls命令的输入参数列表,最后执行ls操作。

      注意虽然一个命令行中允许输入的参数有很多,但这并不表示可以无限输入,也存在命令行过长导致shell编辑器无法承受的情况。如果命令行中包含的输入参数太多而超过了系统支持的最大长度,xargs只会尽可能对最大数量的参数执行指定操作,并不断重复这一过程直到所有标准输入全部处理完毕。在xargs命令后面添加--show-limits选项,即可知道命令行最大能承受的参数数量。

      类UNIX操作系统允许文件名里面有签入的空格(甚至换行符!),这会给像xargs这些为其他程序创建参数列表的命令带来一些问题。因为内嵌的空格可能会被当做分隔符,而要执行的操作命令可能会把空格隔开的单词当做不同的输入参数来处理。为了解决这个问题,find命令和xargs命令允许使用空字符作为参数之间的分隔符。在ASCII码中,空字符是用数字0代表的字符表示(而空格符在ASCII码中是由数字32代替)。find命令提供-print0这一action选项来产生以空字符作为各参数之间分隔符的输出结果,而xargs命令则有-null参数选项支持xargs接收以空字符为参数分隔符的输入。实例如下:

      shell> find ~ -type f -iname '.mysql*' -print0 | xargs --null ls -l

      使用这一特性,可以确保所有的文件包括那些文件名中包含内嵌空格的文件也可以得到特殊的处理。执行结果如下图所示:

 

posted @ 2015-03-06 11:10  jplatformx  阅读(239)  评论(0编辑  收藏  举报