摘要:这一章主要讲如何在Linux系统中查找文件。

查找文件

在这一章中,我们将察看两个用来在系统中查找文件的工具。这些工具是:

  • locate - 通过名字查找文件
  • find - 在一个目录层次结构中搜索文件

也会学习一个经常与文件搜索命令一起使用的命令,它用来处理搜索到的文件列表:

  • xargs - 从标准输入生成和执行命令

另外将介绍两个命令以便在我们探索的过程中协助我们:

  • touch - 更改文件时间
  • stat - 显示文件或文件系统状态

locate - 查找文件的简单方法

locate程序会执行一次快速的路径名数据库搜索,并且输出每个与给定子字符串相匹配的路径名。

例如,我们想要找到所有名字以zip开头的程序。因为我们正在查找程序,可以假定包含程序的目录以 bin/ 结尾。因此,我们试着以这种方式使用 locate命令,来找到我们的文件,locate 命令将会搜索它的路径名数据库,输出任一个包含字符串bin/zip的路径名:

xuxg@xuxg-ubuntu:~$ locate bin/zip
/snap/vlc/1397/usr/bin/zipdetails
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipdetails
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit

find - 查找文件的复杂方式

find程序能基于各种各样的属性搜索一个给定目录(以及它的子目录),来查找文件。

最简单的使用方式中,find 命令接收一个或多个目录名来执行搜索。

例如,输出我们的家目录的路径名列表(包括文件及目录)。

xuxg@xuxg-ubuntu:~$ find ~

这将产生一张很大的列表。因为这张列表被发送到标准输出,我们可以把这个列表管道到其它的程序中。让我们使用 wc 程序来计算出文件的数量:

xuxg@xuxg-ubuntu:~$ find ~ | wc -l
97746

find命令还通过许多应用选项、测试条件和操作实现更复杂的功能。

Tests

比如说我们想在我们的搜索中得到目录列表。我们可以添加以下测试条件:

xuxg@xuxg-ubuntu:~$ find ~ -type d | wc -l
8312

添加测试条件-type d 限制了只搜索目录。相反地,我们可以使用这个测试条件来限定搜索普通文件:

xuxg@xuxg-ubuntu:~$ find ~ -type f | wc -l
89127

这里是 find 命令支持的常见文件类型测试条件:

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

我们也可以通过加入一些额外的测试条件,根据文件大小和文件名来搜索:让我们查找所有文件名匹配通配符模式“*.JPG”和文件大小大于 1M 的普通文件:

xuxg@xuxg-ubuntu:~$ find ~ -type f -name "*.JPG" -size +1M | wc -l
840

在这个例子里面,我们加入了 -name 测试条件,后面跟通配符模式。注意,我们把它用双引号引起来,从而阻止 shell 展开路径名。紧接着,我们加入 -size 测试条件,后跟字符串“+1M”。开头的加号表明我们正在寻找文件大小大于指定数的文件。若字符串以减号开头,则意味着查找小于指定数的文件。若没有符号意味着“精确匹配这个数”。结尾字母“M”表明测量单位是兆字节。下面的字符可以被用来指定测量单位:

字符 单位
b 512 个字节块。如果没有指定单位,则这是默认值。
c 字节
w 两个字节的字
k 千字节 (1024 个字节单位)
M 兆字节 (1048576 个字节单位)
G 千兆字节 (1073741824 个字节单位)

find 命令支持大量不同的测试条件。下表是列出了一些常见的测试条件。请注意,在需要数值参数的情况下,可以应用以上讨论的“+”和“-”符号表示法:

测试条件 描述
-cmin n 匹配内容或属性最后修改时间正好在 n 分钟之前的文件或目录。指定少于 n 分钟之前,使用 -n,指定多于 n 分钟之前,使用 +n。
-cnewer file 匹配内容或属性最后修改时间晚于 file 的文件或目录。
-ctime n 匹配内容和属性最后修改时间在 n*24 小时之前的文件和目录。
-empty 匹配空文件和目录。
-group name 匹配属于一个组的文件或目录。组可以用组名或组 ID 来表示。
-iname pattern 就像-name 测试条件,但是不区分大小写。
-inum n 匹配 inode 号是 n 的文件。这对于找到某个特殊 inode 的所有硬链接很有帮助。
-mmin n 匹配内容被修改于 n 分钟之前的文件或目录。
-mtime n 匹配的文件或目录的内容被修改于 n*24 小时之前。
-name pattern 用指定的通配符模式匹配的文件和目录。
-newer file 匹配内容晚于指定的文件的文件和目录。这在编写执行备份的 shell 脚本的时候很有帮。每次你制作一个备份,更新文件(比如说日志),然后使用 find 命令来判断哪些文件自从上一次更新之后被更改了。
-nouser 匹配不属于一个有效用户的文件和目录。这可以用来查找属于被删除的帐户的文件或监测攻击行为。
-nogroup 匹配不属于一个有效的组的文件和目录。
-perm mode 匹配权限已经设置为指定的 mode 的文件或目录。mode 可以用八进制或符号表示法。
-samefile name 类似于-inum 测试条件。匹配和文件 name 享有同样 inode号的文件。
-size n 匹配大小为 n 的文件
-type c 匹配文件类型是 c 的文件。
-user name 匹配属于某个用户的文件或目录。这个用户可以通过用户名或用户 ID 来表示。

这不是一个完整的列表。find 命令手册有更详细的说明。

操作符

操作符用来描述测试条件之间的逻辑关系。如查找权限不是 0600 的文件和权限不是 0700 的目录:

xuxg@xuxg-ubuntu:~$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)

这里是操作符列表:

操作符 描述
-and 如果操作符两边的测试条件都是真,则匹配。可以简写为-a。注意若没有使用操作符,则默认使用 -and。
-or 若操作符两边的任一个测试条件为真,则匹配。可以简写为-o。
-not 若操作符后面的测试条件是假,则匹配。可以简写为一个感叹号(!)。
() 把测试条件和操作符组合起来形成更大的表达式。这用来控制逻辑计算的优先级。默认情况下,find 命令按照从左到右的顺序计算。经常有必要重写默认的求值顺序,以得到期望的结果。即使没有必要,有时候包括组合起来的字符,对提高命令的可读性是很有帮助的。注意因为圆括号字符对于 shell 来说有特殊含义,所以在命令行中使用它们的时候,它们必须用引号引起来,才能作为实参传递给 find 命令。通常反斜杠字符被用来转义圆括号字符。

逻辑操作符还有另外一个特性要重点理解。比方说我们有两个由逻辑操作符分开的表达式:

expr1 -operator expr2

在所有情况下,总会执行表达式 expr1;然而操作符将决定是否执行表达式 expr2。这里列出了它是怎样工作的:

expr1 的结果 操作符 expr2 is…
-and 总要执行
-and 从不执行
-or 从不执行
-or 总要执行

这样是为了提高性能。

预定义的操作

find 命令允许基于搜索结果来执行操作。有许多预定义的操作和几种方式来应用用户定义的操作。首先,让我们看一下几个预定义的操作:

操作 描述
-delete 删除当前匹配的文件。
-ls 对匹配的文件执行等同的 ls -dils 命令。并将结果发送到标准输出。
-print 把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是默认操作。
-quit 一旦找到一个匹配,退出。

警告:当使用 -delete 操作时,不用说,你应该格外小心。每次都应该首先用 -print 操作代
替 -delete 测试一下命令,来确认搜索结果。

记住,在每个测试和操作之间会默认应用 -and 逻辑运算符。

用户定义的行为

除了预定义的行为之外,我们也可以调用任意的命令。传统方式是通过 -exec 行为。这个行为像这样工作:

-exec command {
   } ;

这里的 command 就是指一个命令的名字,{} 是当前路径名的符号表示,分号是必要的分隔符表明命令的结束。

提高效率

当 -exec 行为被使用的时候,若每次找到一个匹配的文件,它会启动一个新的指定命令的实例。我们可能更愿意把所有的搜索结果结合起来,再运行一个命令的实例。例如,与其像这样执行命令:

ls -l file1
ls -l file2

我们更喜欢这样执行命令:

ls -l file1 file2

这样就导致命令只被执行一次而不是多次。有两种方法可以这样做。传统方式是使用外部命令 xargs,另一种方法是,使用 find 命令自己的一个新功能。我们先讨论第二种方法。

通过把末尾的分号改为加号,就激活了 find 命令的一个功能,把搜索结果结合为一个参数列表,然后用于所期望的命令的一次执行。再看一下之前的例子,这个例子中:

find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt

每次找到一个匹配的文件,就会执行一次 ls 命令。通过把命令改为:

find ~ -type f -name 'foo*' -exec ls -l '{}' +
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt

虽然我们得到一样的结果,但是系统只需要执行一次 ls 命令。

xargs

xargs 命令会执行一个有趣的函数。它从标准输入接受输入,并把输入转换为一个特定命令的参数列表。对于我们的例子,我们可以这样使用它:

find ~ -type f -name 'foo*' -print | xargs ls -l
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt

这里我们看到 find 命令的输出被管道到 xargs 命令,之后,xargs 会为 ls 命令构建参数列
表,然后执行 ls 命令。

注意:当被放置到命令行中的参数个数相当大时,参数个数是有限制的。有可能创建的命令太长以至于 shell 不能接受。当命令行超过系统支持的最大长度时,xargs 会执行带有最大参数个数的指定命令,然后重复这个过程直到耗尽标准输入。执行带有 –show–limits 选项的 xargs命令,来查看命令行的最大值。

find 命令和 xarg 程序允许使用一个可选的 null 字符作为参数分隔符。

touch - 更改文件时间

touch 命令通常被用来设置或更新文件的访问,更改,和修改时间。然而,如果一个文件名参数是一个不存在的文件,则会创建一个空文件。

find 命令的输出结果是无序的。其顺序由存储设备的布局决定。

stat,是一款加大马力的 ls 命令版本。这个 stat 命令会展示系统对某个文件及其属性所知道的所有信息:

选项

最后,我们有这些选项。这些选项被用来控制 find 命令的搜索范围。当构建 find 表达式的时候,它们可能被其它的测试条件和行为包含,这里有一个最常被使用的选项的列表:

选项 描述
-depth 指示 find 程序先处理目录中的文件,再处理目录自身。当指定-delete 行为时,会自动应用这个选项。
-maxdepth levels 当执行测试条件和行为的时候,设置 find 程序陷入目录树的最大级别数
-mindepth levels 在应用测试条件和行为之前,设置 find 程序陷入目录数的最小级别数。
-mount 指示 find 程序不要搜索挂载到其它文件系统上的目录。
-noleaf 指示 find 程序不要基于自己在搜索 Unix 的文件系统的假设,来优化它的搜索。在搜索 DOS/Windows 文件系统和CD/ROMS 的时候,我们需要这个选项