搜索文件在使用操作系统中是非常频繁的操作,一个系统中可能有成千上万的文件,怎么快速找到想要的文件是提升效率的关键。Linux
中我们可以使用强大的find
命令。find
命令可以说是Linux
中最常用也最复杂的命令,特别是和其他命令一起执行的时候。由于find
具有强大的功能,所以它的选项也非常多,其中大部分都值得我们花时间了解一下。
find
命令的一般形式
find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
上面的find
命令可以简化为:
find pathname -options [-print -exec -ok ... action]
命令参数:
pathname: find命令所要查找的目录路径。例如用 . 表示当前目录,用 / 表示根目录
-print: 将find命令匹配到的文件输出到标准输出
-exec: 将find命令匹配到的文件执行该参数指定的shell命令。相应的命令形式为 'command' {};
-ok: 和 -exec 相同,只不过更安全
find
命令最简单的使用是,搜索一个或多个目录。
$ find .
.
./.android
./.android/adbkey
./.android/adbkey.pub
./.android/androidwin.cfg
./.android/avd
./.android/cache
./.android/cache/api-versions-6.bin
[...]
$
$ find /
/
find: /.fseventsd: Permission denied
/.MobileBackups
find: /.MobileBackups: Permission denied
/.OSInstallerMessages
/.PKInstallSandboxManager
/.PKInstallSandboxManager-SystemSoftware
/.vol
/Applications
/Applications/.DS_Store
/Applications/.localized
/Applications/Adobe Application Manager
/Applications/Adobe Photoshop CC 2014
/Applications/Adobe Photoshop CC 2014/Adobe Photoshop CC 2014.app
[...]
$
也可以一次搜索多个目录:
$ find . /
[...] // 输出结果太多,省略
如果仅仅只是列出某个目录的所有文件就感受不到find
的强大。find
可以通过组合各种选项来组成搜索条件来完成搜索,而且还可以运用各种相关的操作。
测试条件
我们可以根据文件类型来搜索。find
支持的文件类型如下(-type
参数):
文件类型 | 描述 |
---|---|
b | 块设备文件 |
c | 字符设备文件 |
d | 目录 |
f | 普通文件 |
l | 符号链接 |
比如我们只想搜索某个目录下面所有的子目录:
$ find . -type d
.
./.android
./.android/avd
./.android/cache
./.atom
./.atom/.apm
./.atom/.apm/_locks
./.atom/.apm/atom-autocomplete-php
./.atom/.apm/atom-autocomplete-php/0.18.9
./.atom/.apm/atom-autocomplete-php/0.18.9/package
[...]
$
我们也可以只搜索某个目录下的普通文件:
$ find ~ -type f | wc -l
868386
$
也可以根据文件大小来搜索(-size
参数):
字符 | 单位 |
---|---|
b | 512个字节块。如果没有指定单位,则这是默认值。 |
c | 字节 |
w | 两个字节的字 |
k | 千字节(1024字节) |
M | 兆字节(1048576字节) |
G | 千兆字节(1073741824字节) |
我们可以搜索目录下面文件大小大于1G
的文件:
$ find ~ -type f -size +1G
/Documents/Virtual Machines.localized/Windows 10 x64 2.vmwarevm/Virtual Disk-s001.vmdk
./Documents/Virtual Machines.localized/Windows 10 x64 2.vmwarevm/Virtual Disk-s002.vmdk
./Documents/Virtual Machines.localized/Windows 10 x64 2.vmwarevm/Virtual Disk-s003.vmdk
[...]
$
上面的例子使用了文件类型和文件大小一起作为搜索的测试条件,只有文件类型是普通文件而且大小大于1G
的文件才会被搜索到。我们也可以再增加搜索条件:
$ find ~ -type f -name "*.png" -size +1M | wc -l
5543
$
上面的例子中我们使用了-name
测试条件,后面跟通配符模式。注意我们使用双引号括起来,是为了阻止bash
展开星号。上面的测试条件是找到文件名以png
结尾,并且是普通文件类型的文件,而且文件大小要大于1M
。如果在数字参数前面加上+
或者-
则表示大于或者小于指定数。
find
的其他测试条件,下面是一些常见的测试条件,在数值参数的前面可以使用+
或者-
:
测试条件 | 描述 |
---|---|
-cmin n | 匹配的文件和目录的内容或属性最后修改时间正好在 n 分钟之前。 指定少于 n 分钟之前,使用 -n,指定多于 n 分钟之前,使用 +n。 |
-cnewer 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
提供了逻辑操作符来创建复杂的测试条件,也便于阅读和维护。逻辑操作符如下:
操作符 | 描述 |
---|---|
-and | 如果操作符两边的测试条件都是真,则匹配。可以简写为 -a。 注意若没有使用操作符,则默认使用 -and。 |
-or | 若操作符两边的任一个测试条件为真,则匹配。可以简写为 -o。 |
-not | 若操作符后面的测试条件是真,则匹配。可以简写为一个感叹号(!)。 |
() | 把测试条件和操作符组合起来形成更大的表达式。这用来控制逻辑计算的优先级。 |
注意,find
命令从左往右计算,而且要注意圆括号在bash
中要转义
我们可以使用上面的逻辑操作符来构造复杂的测试条件,例如只查找特定某些文件:
find . \(-name "*.pdf" -or -name "*.txt"\)
上面的例子会打印当前目录下以.txt
或.pdf
结尾的文件名。也可以找出目录下不是pdf
的普通文件:
find . ! -name "*.pdf"
逻辑操作符可以描述为下面的简短形式。逻辑操作符还有一种和其他编程语言相同的特性:短路。下面是逻辑操作符的执行情况:
expr1 -operator expr2
expr1的结果 | 操作符 | expr2的执行情况 |
---|---|---|
真 | -and | 总要执行 |
假 | -and | 从不执行 |
真 | -or | 从不执行 |
假 | -or | 总要执行 |
预定义操作
find
除了可以根据特定条件搜索文件外,还可以在搜索到文件之后根据搜索结果执行预定义的操作。find
预定义的常用的操作如下表:
操作 | 描述 |
---|---|
把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是 默认操作。 | |
-delete | 删除匹配到的文件 |
-ls | 对匹配到的结果执行 ls -dils 命令,然后将结果输出到 STDOUT
|
-quit | 一旦找到一个匹配,就退出 |
find .
会输出当前目录的所有文件和子目录,这相对于:
find . -print
也可以使用-delete
来删除搜索到的文件和目录:
find . -empty -delete
find . -type f -name '*.txt' -delete
上面的第一条命令会删除当前目录下的空目录和空文件,第二条命令会删除当前目录下面所有的txt
文件。( 好像例子不是太好:( )
当使用-delete
时得特别小心,可以先用print
输出看看里面有没有不能删除的文件
自定义操作
find
除了可以使用预定义的操作之外,还可以通过-exec
来自定义操作:
find path [expression] -exec command {} ;
上面的-exec
是表示自定义操作的开始,command
就是shell
命令的名字,{}
是当前路径名的符号表示,分号是命令结束的标志。
find . -type f -name "*.zip" -size +100M -exec rm {} \;
删除当前目录下大于100M
的压缩文件。上面命令中的{}
代表find
找到的文件路径,find
的结果会放到{}
位置之中作为rm
的参数。
find . -type f -name ".pdf" -exec ls -l {} \;
上面的命令用于输出当前目录下所有的pdf
文件详情。当使用-exec
时,每找到一个匹配就会执行一次-exec
后面的命令。有时候我们可能希望将find
的结果作为一个整体然后再运行额外的命令,这个时候就可以使用外部命令xargs
。xargs
从标准输入接受输入,并把输入转换为一个特定命令的参数列表,这样命令就不用每匹配到一个文件或目录就执行一次。
find ~ type f -name "*.pdf" | xargs ls -l
因为xargs
接受标准输入,所以把find
的结果用管道(pipe
)到xargs
的输入,xargs
会把输入构造为ls
的参数列表,然后执行ls
命令。这种方式比-exec
方式快很多,ls
执行的次数远远小于-exec
的匹配到文件就执行一次命令。
xargs
后面的命令并不是只执行一次,这取决于输入的参数的个数,如果超过了bash
能够处理的最大长度,xargs
会使用最大的参数长度执行后面的命令,然后重复这个过程,直到所有的输入都执行完。