find 实用程序

find 是 Unix 系统里面最强大和最有用的实用程序之一。他可以匹配文件名称、 类型以及修改时间等并对匹配到的文件执行相关操作。事实上,很多时候,find 都不是单纯用来查找文件的。如果仅仅要查找文件,通常用一个更快速的 locate 工具。

使用简介

find path operators

path 是指定一个或者多个 find 将在其中进行查找的目录。而 operator 可以 有很多选择,比如:

-name filename
查找匹配 filename 的文件。
-perm mode
查找指定权限的文件,文件权限必须以八进制形式给出。
-type c
查找指定类型的文件, c 可以是代表普通文件的 'f' 、代表块设备的 'b' 或者 代表软链接的 'l' 等。
-user name
查找属于用户 name 的文件。 name 可以通过用户名或者用户 id 来指定。
-group name
查找属于组 name 的文件。 name 可以通过组名称或者组 id 来指定。
-size n
查找 n 块那么大的文件,每一块通常是 512 字节。 +n 表示大于 n 块,而 nc 表示 n 个字符那么长。
-inum n
查找 inode 号为 n 的文件。
-atime n
查找 n 天前访问过的文件。 +n 表示``超过 n 天''而 -n 表示``小于 n 天'' 。
-mtime n
-atime 类似,不过检查文件内容被修改的时间。
-ctime n
类似于 -atime ,不过检查 inode 状态上一次被改变的时间。
-newer file
查找修改时间比 file 要新的文件。

可以把多个 operator 组合起来:

operator1 -a operator2
查找满足 operator1operator2 的文件。事实上, -a 是可以省略的,因为 他是 operator 之间的默认连接关系。
operator1 -o operator2
查找满足 operator1 或者 operator2 的文件。
!operator
查找不满足 operator 的文件。
\( expression \)
把一些东西组合起来当作一个单元来看待。这儿的 \ 主要是用于转义,以免被 shell 解释掉括号了。

另外,还有一些 operator 用于告诉 find 如何对待找到的文件。

-print
在标准输出打印文件名。这通常是默认的操作。
-ls
用一个类似与 ls -l 的格式列在标准输出出文件名。
-exec command
执行 command 。使用 {} 来表示匹配到的文件名。 command 必须以 \; 来标记 结束。如 find . -name '*.o' -exec rm -f {} \;
-ok command
-exec 一样,不过他在执行命令之前会先向你询问是否要执行。

查找特定名称的文件

在匹配文件名的时候可以使用通配符。比如:

find . -name '*.o' -print

注意 -name 选项指定的通配符只是匹配文件名的部分,而不管路径部分,如果 要指定路径部分的匹配模式,可以用 -wholename 或者 -path 选项。如果是要 跳过某个目录以及其子目录的所有文件,可以使用 -prune 选项,而不是检查那 个目录下面的所有文件,例如:

find . -wholename './src/emacs' -prune -o -print

理解 find

其实只要把每一个查找的参数看成 find 将要进行求值的``逻辑表达式''就很容 易理解了。同时,作为一个逻辑表达式, -print 总是为真,而 -exec-ok 在他们执行的程序返回非零的时候就为真。例如:

find . \! \( -atime +5 \( -name '*.o' -o -name '*.tmp' \) \) -print

首先看括号里面的 -name '*.o' -o -name '*.tmp' 表示文件名以 .o 或者 .tmp 结尾的文件,记为 exp1 。然后再外一层 -atime +5 exp1 ,两个表达式 之间没有指定连接符,因此默认为 -a ,所以表示访问时间大于5天或者满足 exp1 的,记为 exp2 。然后是 \! exp2 ,这个就表示不满足 exp2 的,记为 exp3 。最后就是 exp3 -print 了,两者之间采用默认的 -a 连接,即,只有在 exp3 求值为真的时候才对 -print 进行求值(永远为真)。这样就很好理解了。

理解 find 指定的时间

时间是以天为单位指定的,它有下面三种形式:

要进行更精确的时间匹配,可以使用 touch 来创建一个指定时间的文件,并使 用 -newer 参数。一个例子:

touch -t 199907031046 /tmp/file1
touch -t 200106042137 /tmp/file2
find . -newer /tmp/file1 \! -newer /tmp/file2 -print

事实上,现在 gnu find 还提供了 -mmin 、 -amin 之类的参数用于更精确的时 间匹配。

小技巧

-exec 参数中文件名替换

-exec 中,通常 find 只会替换单独的 {} ,比如 -exec mv {} {}.bak \; 通常是不会如期望的那样工作,这个时候不得不使用一个脚本。不过, gnu find 则会不管是否是单独的 {} 都会替换,这样通常会比较方便。

使用自定义的测试

注意 -exec 在程序返回非零的时候才为真,于是,你可以使用自己的测试,例 如:

find . -name '*.[ch]' -exec my_test {} \; -print

通常 -exec 放得越靠后越好,因为每一次测试都会启动一个新的进程,而有多 个条件的时候按照逻辑运算的特性很多时候前面的结果可以直接影响是否有必要 对后面的 -exec 进行求值。