查找文件
摘要:这一章主要讲如何在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 命令。并将结果发送到标准输出。 |
把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是默认操作。 | |
-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 的时候,我们需要这个选项 |