一个典型的Linux系统有大量的文件。如何在Linux查找文件呢?我们已经知道Linux文件系统是按照惯例从一个类Unix世代传递到下一个世代相传的,组织得很好,但是文件数量庞大,可能会带来令人生畏的问题。
以下是两个可以用来在系统钟寻找文件的两个工具:
- locate —Find files by name.
- find —Search for files in a directory hierarchy.
以及经常和搜索文件命令一起使用来处理文件列表结果的命令:
- xargs —Build and execute command lines from standard input.
此外,还将引入几个命令:
- touch —Change file times.
- stat —Display file or filesystem status.
locate--使用简单方式寻找文件
locate程序路径名执行快速数据库搜索然后输出每一个包含给定子字符串的文件名称。例如,想要查找所有以zip开头的程序。因为是查找程序,可以假定包含程序的目录以 bin/作为结尾。因此,可以使用下面方法来查找文件:
[me@linuxbox ~]$ locate bin/zip
locate 将会查找路径名称数据库并输出包含 bin/zip 字符串的路径名称:
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
如果查找条件不简单,locate 程序也可以与其他工具一起使用,如 grep:
[me@linuxbox ~]$ locate zip | grep bin
/bin/bunzip2
/bin/bzip2
/bin/bzip2recover
/bin/gunzip
/bin/gzip
/usr/bin/funzip
/usr/bin/gpg-zip
/usr/bin/preunzip
/usr/bin/prezip
/usr/bin/prezip-bin
/usr/bin/unzip
/usr/bin/unzipsfx
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
locate 程序已经存在多年了,并且有几种不同的变体被普遍使用。在现代Linux发行版本中最常用的两个是 slocate 与 mlocate,尽管通常通过名为 locate 的符号链接来访问它们。locate 不同版本具有相同的选项集。一些版本提供正则表达式以及通配符支持。查看操作手册来确认locate版本。
locate 程序使用的数据库是从哪里来的?
在一些发行版本上,在系统安装后立刻执行 locate 程序会失败,但是第二天再次尝试就能够执行成功。locate 数据库 是另外一个名为 updatedb 程序所创建的。通常来讲,通常,它作为计划工作定期运行;即,cron 守护进程以固定时间执行的任务。大多数系统配置为 locate 程序运行 updatedb 每天一次。因为数据库并不是实时更新,你将会注意到使用 locate 程序时 最近的文件不会出现在结果中。为了解决这个问题,可以通过以超级管理员在提示中运行 updatedb 程序的方式 来手动地执行 updatedb 程序。
find -- 以复杂的方式来查找文件
locate 程序是单纯地根据 名称来查找文件,但是 find 程序 则是通过查找给定的文件夹(以及子文件夹)中文件的多种属性来完成。
最简单的用途是为find提供一个或多个要搜索的目录名称。 例如,它可以输出我们主目录的列表:
[me@linuxbox ~]$ find ~
在大多数活跃的用户账户上,这条命令会产生很长的列表。 因为此列表被发送到标准输出,我们可以将列表发送到其他程序中,例如,使用 wc 来统计文件数量:
[me@linuxbox ~]$ find ~ | wc -l
47068
find 的好处在于,它可以用来识别符合特定条件的文件。它通过 tests, actions, and options的应用程序来完成此任务。
Tests
假如我们想要从搜索中得到目录的列表。我们可以加入下面的检验:
[me@linuxbox ~]$ find ~ -type d | wc -l
1695
加上测试 -type d 来限制为搜索文件夹。相反地,也可以通过下面检验限制为正常的文件:
[me@linuxbox ~]$ find ~ -type f | wc -l
38737
表17-1列出了 find 支持的 文件类型 检验
Table 17-1: find File Types
| File Type | Description |
|---|---|
| b | Block special device file |
| c | Character special device file |
| d | Directory |
| f | Regular file |
| l | Symbolic link |
我们还可以通过添加一些其他测试来按文件大小和文件名进行搜索。来查找所有能够匹配 *.JPG通配符模式以及大于 1 megabyte 文件:
[me@linuxbox ~]$ find ~ -type f -name "*.JPG" -size +1M | wc -l
840
在这个例子中,我们增加 -name 检验 后跟 通配符模式。请注意,我们将其用引号引起来,以防止shell 扩展路径名。接下来,我们增加 -size 检验 后跟 +1M 字符。前面的 加号 表示 我们正在查找大于指定数字的文件。如果前面是减号将会变为 小于给定数字 的含义。无符号则表示 值完全匹配。最后面的字符 M 指测量单位为兆字节。下表为单位:
Table 17-2: find Size Units
| Character | Unit |
|---|---|
| b | 512-byte blocks (the default if no unit is specified) |
| c | Bytes |
| w | 2-byte words |
| k | Kilobytes (units of 1024 bytes) |
| M | Megabytes (units of 1,048,576 bytes) |
| G | Gigabytes (units of 1,073,741,824 bytes) |
find 支持许多不同的验证。表17-3提供了经常使用的验证。请注意,在需要数字参数的情况下,可以如同上述讨论的使用 + 号或者 - 号。
Table 17-3: find Tests
| Test | Description |
|---|---|
| -cmin n | Match files or directories whose content or attributes were last modified exactly n minutes ago. To specify fewer than n minutes ago, use -n ; to specify more than n minutes ago,use +n . |
| -cnewer file | Match files or directories whose contents or attributes were last modified more recently than those of file |
| -ctime n | Match files or directories whose contents or attributes (i.e., permissions) were last modified n *24 hours ago. |
| -empty | Match empty files and directories. |
| -group name | Match file or directories belonging to group name . name may be expressed as either a group name or as a numeric group ID. |
| -iname pattern | Like the -name test but case insensitive. |
| -inum n | Match files with inode number n . This is helpful for finding all the hard links to a particular inode. |
| -mmin n | Match files or directories whose contents were modified n minutes ago. |
| -mtime n | Match files or directories whose contents only were last modified n *24 hours ago. |
| -name pattern | Match files and directories with the specified wildcard pattern . |
| -newer file | Match files and directories whose contents were modified more recently than the specified file . This is very useful when writing shell scripts that perform file backups. Each time you make a backup, update a file (such as a log) and then use find to determine which files have changed since the last update. |
| -nouser | Match file and directories that do not belong to a valid user. This can be used to find files belonging to deleted accounts or to detect activity by attackers. |
| -nogroup | Match files and directories that do not belong to a valid group. |
| -perm mode | Match files or directories that have permissions set to the specified mode . mode may be expressed by either octal or symbolic notation. |
| -samefile name | Similar to the -inum test. Matches files that share the same inode number as file name |
| -size n | Match files of size n |
| -type c | Match files of type c |
| -user name | Match files or directories belonging to name . name may be expressed by a username or by a numeric user ID. |
可以查看 man page 查看完整列表
Operators
为了能够描述在验证条件之间的逻辑关系,可以使用 逻辑操作符;例如,查找所有 权限不是0600 的 文件以及 权限不是0700 的文件夹:
[me@linuxbox ~]$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm
0700 \)
Table 17-4: find Logical Operators
| Operator | Description |
|---|---|
| -and | Match if the tests on both sides of the operator are true. May be shortened to -a . Note that when no operator is present, -and is implied by default. |
| -or | Match if a test on either side of the operator is true. May be shortened to -o |
| -not | Match if the test following the operator is false. May be shortened to -! . |
| ( ) | Groups tests and operators together to form larger expressions. This is used to control the precedence of the logical evaluations. By default, find evaluates from left to right. It is often necessary to override the default evaluation order to obtain the desired result. Even if not needed, it is helpful sometimes to include the grouping characters to improve readability of the command. Note that since the parentheses characters have special meaning to the shell, they must be quoted when using them on the command line to allow them to be passed as arguments to find . Usually the backslash character is used to escape them. |
解构 上面的 find 命令,可以把条件分为以 -or 的两组:
(expression 1) -or (expression 2)
如果同时查找文件和目录,为什么要使用-or而不是-and?因为 当 find 扫描文件与文件夹时,需要对比是否满足给定的条件。它是否满足 具有错误权限的文件 和 具有错误权限的文件夹 其中的 某项条件。但不能被同时满足,因此可以如此解释表达式:
(file with bad perms) -or (directory with bad perms)
上面例子中我们定义 0600 作为文件的“好”权限,0700作为文件夹的“好”权限:
-type f -and -not -perms 0600
-type d -and -not -perms 0700
在 17-4表中,可以看到 -and 是默认的,因此可以把它放心的移除:
find ~ (-type f -not -perms 0600) -or (-type d -not -perms 0700)
然而,因为括号对于shell有特殊含义,必须将其转义来防止shell解释它们。在前面加上反斜线符号。
逻辑符号还有其他特点,如下面命令:
expr1 -operator expr2
在所有例子中,expr1总是执行;然而 操作符将决定了 是否执行expr2。
Table 17-5: find AND/OR Logic
| Results of expr1 | Operator | expr2 is... |
|---|---|---|
| True | -and | Always performed |
| False | -and | Never performed |
| True | -or | Never performed |
| False | -or | Always performed |
Actions
执行 find 可以得到文件的列表,find也支持对结果中的每一项做操作:
Table 17-6: Predefined find Actions
| Action | Description |
|---|---|
| -delete | Delete the currently matching file. |
| -ls | Perform the equivalent of ls -dils on the matching file. Output is sent to standard output. |
| Output the full pathname of the matching file to standard output. This is the default action if no other action is specified. | |
| -quit | Quit once a match has been made. |
在开始时候我们执行了:
find ~
此命令结果是文件列表。产生列表的原因是由于 如果没有指定其它的操作就会应用 -print,因此,可以等价于:
find ~ -print
可以使用 find 来删除满足条件的文件,例如,删除扩展名为 .BAK 文件:
find ~ -type f -name '*.BAK' -delete
上面命令,可以删除所有在 用户 home文件夹(以及子文件夹下)以 .BAK结尾的文件。
【注】:使用 -delete 之前最好 使用-print来确认删除文件。
有下面命令:
find ~ -type f -name '*.BAK' -print
此命令查找(-type f)每个以 .BAK 作为扩展名 ( -name '*.BAK' )普通文件,然后输出到标准输出( -print )。然而,命令如此执行原因是取决于每个条件以及操作的逻辑关系。因为 -and 在每个条件以及操作间是默认的,等价于下面命令:
find ~ -type f -and -name '*.BAK' -and -print
Table 17-7: Effect of Logical Operators
| Test/Action | Is performed when... |
|---|---|
| -type f and -name '*.BAK' are true. | |
| -name '*.BAK' | -type f is true. |
| -type f | Is always performed, since it is the first test/action in an -and relationship. |
操作和条件的执行顺序是重要的,例如以下命令:
find ~ -print -and -type f -and -name '*.BAK'
此命令将会打印每个文件 然后再判断文件类型以及文件扩展名。
User-Defined Actions
除了执行预定义的操作,也可以执行任意的命令。传统的方法是使用-exec操作,例如:
-exec command {} ;
其中command是命令的名称,{}是当前路径名的符号表示,分号是指示命令结束的必需定界符。例如,如同 -delete 一样删除文件:
-exec rm '{}' ';'
同样,由于花括号和分号字符对 shell 具有特殊含义,因此必须将其引号或转义
也可以交互执行用户定义的操作。使用 -ok 来代替 -exec ,在执行指定命令之前用户会被提示:
find ~ -type f -name 'foo*' -ok ls -l '{}' ';'
< ls ... /home/me/bin/foo > ? y
-rwxr-xr-x 1 me me 224 2011-10-29 18:44 /home/me/bin/foo
< ls ... /home/me/foo.txt > ? y
-rw-r--r-- 1 me me 0 2012-09-19 12:53 /home/me/foo.txt
提升运行效率
当 使用 -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 2011-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2012-09-19 12:53 /home/me/foo.txt
改变命令为:
find ~ -type f -name 'foo*' -exec ls -l '{}' +
-rwxr-xr-x 1 me me 224 2011-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2012-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 2011-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2012-09-19 12:53 /home/me/foo.txt
【注】:尽管可以放入命令行的参数数量非常多,但数量并不是无限的。 当命令行超过系统支持的最大长度时,xargs将使用可能的最大参数数量执行指定的命令,然后重复此过程,直到用完标准输入为止。 要查看命令行的最大大小,使用--show-limits选项执行xargs。
处理有趣的文件名称
类Unix在文件名内允许内嵌空格(甚至换行)。只会导致xargs 组织参数列表给其他程序时候出现问题。嵌入的空格将被视为分割符,并且生成的命令会将每个空格分隔的单词解释为单独的参数。为了解决这个问题,find和xarg允许可选使用空字符作为参数分隔符。空字符在ASCII中定义为由数字零表示的字符(相对的,空格在ASCII中定义为32).find 程序提供 -print0 操作,这将会导致结果以null作为分隔符的输出,xargs 命令 有 --null 选项,该选项接受以null分隔的输入:
find ~ -iname '*.jpg' -print0 | xargs --null ls -l
用这个方法可以正确地处理包含空格的文件名。
练习
在 playground 文件夹下创建大量的子文件夹:
[me@linuxbox ~]$ mkdir -p playground/dir-{00{1..9},0{10..99},100}
[me@linuxbox ~]$ touch playground/dir-{00{1..9},0{10..99},100}/file-{A..Z}
使用两行代码我们创建了 100 * 26 子文件。
其中,我们创建了100个file-A文件:
[me@linuxbox ~]$ find playground -type f -name 'file-A'
find 产生的列表不是顺序的。其顺序由存储设备的布局确定。可以用下面命令统计数量:
[me@linuxbox ~]$ find playground -type f -name 'file-A' | wc -l
100
下一步,根据修改时间来查找文件。为此,我们将首先创建一个参考文件,我们将与之比较修改时间:
[me@linuxbox ~]$ touch playground/timestamp
现在此文件修改时间为创建时间,可以使用 stat 命令来确认,stat命令显示系统对文件及其属性的所有了解:
[me@linuxbox ~]$ stat playground/timestamp
File: `playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2012-10-08 15:15:39.000000000 -0400
Modify: 2012-10-08 15:15:39.000000000 -0400
Change: 2012-10-08 15:15:39.000000000 -0400
如果再次执行 touch 后执行 stat:
[me@linuxbox ~]$ touch playground/timestamp
[me@linuxbox ~]$ stat playground/timestamp
File: `playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ me) Gid: ( 1001/ me)
Access: 2012-10-08 15:23:33.000000000 -0400
Modify: 2012-10-08 15:23:33.000000000 -0400
Change: 2012-10-08 15:23:33.000000000 -0400
下一步,使用 find 来更新playground文件夹中的一些文件:
[me@linuxbox ~]$ find playground -type f -name 'file-B' -exec touch '{}' ';'
接下来,我们将会使用 find 通过 将所有文件与参考文件时间戳进行比较 来确认更新的文件
[me@linuxbox ~]$ find playground -type f -newer playground/timestamp
因为都通过touch进行了更新,因此可以通过-newer测试进行识别。
最后,返回到“好权限”例子:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
此命令由于所有文件都不满足“好文件”,因此列出了 100+100*26+1+1(timestamp以及playground本身)=2702个文件。我们可以使用下面命令来给每个文件赋权:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec chmod 0600
'{}' ';' \) -or \( -type d -not -perm 0700 -exec chmod 0700 '{}' ';' \)
在日常情况下,我们可能会发现发出两个命令(一个用于目录,一个用于文件)比这个大的复合命令更容易,这里的重点是要了解如何将操作符和操作一起使用来执行有用的任务。
Options
使用下面选项可以控制 find 的作用范围:
Table 17-8: find Options
| Option | Description |
|---|---|
| -depth | Direct find to process a directory’s files before the directory itself. This option is automatically applied when the -delete action is specified. |
| -maxdepth levels | Set the maximum number of levels that find will descendinto a directory tree when performing tests and actions. |
| -mindepth levels | Set the minimum number of levels that find will descend into a directory tree before applying tests and actions. |
| -mount | Direct find not to traverse directories that are mounted on other filesystems. |
| -noleaf | Direct find not to optimize its search based on the assumption that it is searching a Unix-like filesystem. This is needed when scanning DOS/Windows file- systems and CD-ROMs. |