Linux find 命令

find 命令是在指定目录下搜索文件,并向标准输出打印文件的信息。例如

find  work/

find 命令会打印 work 目录下的所有文件的信息。

在类Unix系统中,一切皆文件,因此这里的文件指普通文件,目录,符号链接等等。

语法

find 最常用的语法如下

find [directory...] [expression]

directory 表示要搜索的目录,可以有多个目录,expression 是表达式。例如

find work/ -name test.c

work 就是要搜索的目录,-name test.c 是表达式。

表达式

find 表达式,由以下几个元素( primaries )中的任意几个组成

  1. 选项(Options)。选项会影响全局的行为,例如 -regextype 指定使用的正则表达式。
  2. 测试(Tests) : 判断文件的属性,返回 true 或 false。例如,通过 -name 判断文件名。
  3. 行为(Actions) : 用于执行某个动作,返回 true 或 false。例如删除匹配的文件。
  4. 操作符(Operators) : 用于给测试和行为添加逻辑操作符,也就是与、或、非。

选项

选项会影响全局的行为,这里介绍几个常用的选项。

-regextype

-regextype 选项用于指定正则表达式的类型,值有 emacs 、posix-awk 、posix-basic 、 posix-egrep 、 posix-extended。find 默认使用 GNU emacs 正则表达式。

正则表达式类型五花八门,我们不可能去记住所有,但是 POSIX 正则表达式却是通用且可移植的,因为如果一旦要在 find 命令中使用正则表达式,那么我们把 -regextype 设置为 posix-basic 或 posix-extended。


-depth

-depth 选项表示表示优先处理目录下的内容,再处理目录本身。

注意 -depth 并不是表示处理目录的深度。


-mindepth, -maxdepth

-mindepth / -maxdepth 都是基于 find 搜索的起始目录,指定搜索的最小 / 最大的目录深度。

$ find out -maxdepth 4 -name userdata.img
out/target/product/msm8937_32/userdata.img

在 out 目录下有两个 userdata.img ,但是我们只需要其中一个,因此可以指定搜索的最大的目录深度。

这里有个小支持,目标文件在第几层目录下,-maxdepth 就指定相应的数字即可。


测试

find 表达式中的测试,是针对文件属性的测试,这里列出常用的测试项。

-name

-name pattern 对基本文件名进行匹配, pattern 使用通配符来匹配文件名。

可以参考GNU官网解释的 通配符

下面的命令是在当前目录下搜索基本文件名是 test.c 的文件。

find . -name test.c

下面的命令是搜索基本文件名以 .java 结尾的文件

find . -name \*.java

注意,为了防止 shell 扩展,需要把通配符( 这里指星号 )进行转义或者添加引号。

-iname 是 -name 忽略大小写的版本。


-path

-path pattern 把文件的全路径名进行匹配,参数 pattern 使用通配符进行匹配。

由于 -path 匹配的是带有全路径的文件名,而不是匹配目录,因此在 pattern 字符串最后使用斜线( / )是无法匹配任何文件的。

下面的命令是在 work 目录下找到 test 的文件。

find work -path work/test

这个 test 文件可以是文件,也可能是目录。


-regex

-regex pattern 与 -path pattern 的相似之处是,它们都使用文件的全路径进行匹配,但是 -regex 的 pattern 使用的是正则表达式匹配,而 -path 的 pattern 使用的是通配符进行匹配。

正如前面所说,find 命令默认使用 GNU emacs 正则表达式,但是可以通过 -regextype 设置自己熟悉的正则表达式,通常我们使用可移植的 POSIX 正则表达式。

find work/ -regextype posix-basic -regex '.*\.c'

这个命令是在 work 目录下搜索 .c 结尾的文件,通过 -regextype 设置 POSIX 基本正则表达式,通过 -regex 设置正则表达式。

GNU官网上有关于所有 正则表达式 的简单介绍,但是是高度概括。


-type

-type 用来测试文件类型,例如 -type f 测试文件是否是普通文件,-type d 测试文件是否是目录。

比较下面两个命令的异同

find . -name \*.bak -type f
find . -type f -name \*.bak

两个命令都是找出基本名以 .bak 结尾的的普通文件,但是性能有所差异。

第一个命令,是先找出基本名以 .bak 结尾文件,再判断文件类型是否为普通文件。

第二个命令,是先判断文件类型是否为普通文件,再找出基本名以 .bak 结尾文件。

类型判断会调用stat() 函数,第一个命令只对 .bak 结尾的文件调用这个函数,而第二个命令会为所有的文件调用这个函数。

如果一个目录下有大量的文件和目录,那么这个性能差异就能体现出来。


行为

find 表达式中的行为可以对匹配的文件进行某种操作。

-print

-print 会文件路径打印到标准输出,也就是屏幕上。

对于 -print ,我们尤其要注意的一点是

-print 行为默认会在匹配的文件上执行,除非表达式包含除了 -prune 和 -quit 行为以外的其它行为。

这一点是理解复杂的 find 命令的关键。

fprint file 会把结果输出到文件 file 中,而不是标准输出。


-print0

-print0 也是把文件路径打印到标准输出,但是输出结果以空字符(ASCII码为0)分隔,而-print的输出结果是以换行符分隔。

-print 的输出结果如下

$ find . -name \*.c
./hello.c
./test.c

虽然省略了-print 行为,但是还记得刚才说过的吗,系统默认会使用 -print 对匹配的文件进行打印。

-print0 输出结果如下

$ find . -name \*.c -print0
./hello.c./test.c

可以看到两个名子连接到了一起,其中它们是以空字符分隔的,只是屏幕上没有显示出来而已。

那么 -print0 有什么用呢?

类 Unix 系统允许除了空字符( ASCII码为0 )和斜线( / )以外的任意字符出现在文件名中。也就是说文件名中可能出现空白字符,例如空格,制表符,换行符。

find 命令如果使用 -print 行为,那么生成的结果是以换行符分隔的,然而如果此时文件名中包含换行符,并且 find 结果管道到另外一个程序,那么这个程序就无法正确处理。

为了解决这个问题,GNU 给 find 命令增加了 -print0 行为,它把输出的结果以空字符(不是空白字符,ASCII码为0)进行分隔。对于接收 find 结果的程序呢,它也需要指定分隔符。例如 xargs 可以通过 -0 选项把分隔字符指定为空字符。如此一来,就可以正确处理文件名中有换行符的情况。

find . -name \*.bak -print0 | xargs -0 /bin/rm

-ls

-ls 行为使用 ls -dils 打印结果到标准输出。

-fls file 会把结果输出到 file 中。


-prune

如果使用了 -prune 行为,并且处理的是目录,那么这个行为将会返回 true,并且 find 命令不会进入这个目录搜索。

-prune 通常用来跳过某个目录,例如

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

我们来理解一下这个命令,因为我刚开始也疑惑了好久,首先表达式中有了 -print 行为,因此系统不会再使用默认的-print 打印匹配的文件。

然后,如果 -path './src/emacs' -prune 为 true,代表目录为 emacs,并且此时不会进入这个目录进行搜索。由于此时不会执行后面的 -o -print ,因此就算匹配了 emacs 目录,也不会打印这个目录以及目录下的内容。

如果 -path './src/emacs' -prune 为 false,那么就证明不是 emacs 目录,那么就会执行 -o 后面的 -print 行为。

因此,这个命令的整体效果就是跳过 emacs 目录,并打印其它目录以及目录下的内容。

如果我们还要打印 emacs 目录,只需要在后面加上一个 -print 即可

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

或者

find . -path '.src/emacs' -prune  , -print

逗号也是 find 表达式中的操作符,它会执行前,后两个动作


-delete

-delete删除匹配的文件或目录。

find . -name \*.bak -delete

这个命令会删除当前目录下以.bak结尾的文件。

1.删除动作永远是危险的,因为不可恢复,所以在删除之前,最好查看一下要删除哪些文件。
2.-depth-prune,这两个行为是冲突的,因为-depth表示先处理目录下内容,再处理目录,而-prune不会进入目录。-delete隐式包含-depth选项,因此-delete不能与-prune一起使用。


-exec, -execdir

-exec command {} ; 对每一个匹配的文件执行 command 命令。

其中 {} 会被替换为正在处理的文件名,; 表示命令结束。由于 {} 和 ; 对 shell 来说有特殊含义,因此需要对它们转义或者加引号。

例如下面命令会把 work 目录下的所有c文件复制到 dest 目录下。

find work/ -name \*.c -exec cp '{}' dest/ ';'

-exec command {} ; 有一个小小的性能问题,那就是针对每一个文件,都会调用一次命令。而 -exec command {} + 会把所有的文件名添加到命令的末尾,只执行一次命令。

因此上面的命令可以更改如下

find work/ -name \*.c -exec cp -t dest/ '{}' +

这个命令有几点需要注意

  1. 加号 ( + ) 不会被 shell 特殊对待,因此不用转义或者加引号。
  2. {} + 作为一个整体,{} 被替换为文件列表,+ 代表命令结束,中间不能添加任何字符。因此 cp 命令必须把目标目录以 -t dest 的形式,在 {} + 之前添加。

出于安全考虑,可以把 -exec 替换为 -execdir。


-ok ,-okdir

-ok command {} ; 的功能与 -exec command {} ; 相似,都会为每个匹配的文件执行一次命令,但是 -ok command {} ; 会询问用户是否执行。

出于安全考虑,可以把 -okdir command {} ; 替换为-ok command {}; 。

但是令人奇怪的时,没有 -ok command {} + 或 -okdir command {} + 行为,这样就不能询问用户是否对所有文件执行一次命令,但是我们可以通过 xargs 的 -p / --interactive 选项来完成这种工作。

find work/ -name '*.c' | xargs -p ls -l

这个命令会先查询 work 目录所有 c 文件,然后询问用户是否对这些文件执行一次 ls -l 命令。


-quit

-quit 行为表示立即退出。

例如在某个目录下,可能有几个相同的文件,但是我们只需要其中一个,可以使用如下命令

find work/ -name hello.c -quit

操作符

以下列出与POSIX兼容的操作符,以降序优先级排序

  1. ( expr ) : 用于提升优先级。括号对 shell 有特殊意义,因此需要转义或加引号。
  2. ! expr : 非操作,!也需要转义或者加引号。
  3. expr1 -a expr2 : 与操作,如果省略操作符,默认也是与操作,例如 expr1 expr2。如果 expr1 返回 false,不会执行 expr2。
  4. expr1 -o expr2 : 或操作。如果 expr1 返回 true,那么不会执行 expr2。
  5. expr1 , expr2 : 表达式expr1expr2 都会被计算,但是整体表达式的值的返回值由 expr2 决定。

结束

find 命令其实还有很多内容,在需要的时候,可以通过 man find 查询 find 手册。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值