📘 1. find 简介
find
是 Linux/Unix 系统中用于在目录树中搜索文件的强大工具。可以按文件名、类型、权限、大小、时间等多种条件查找文件,也可以在找到文件后执行操作,如打印、删除、添加到版本控制等。
⚙️ 2. find 用法
🔨 2.1 基础用法
find [基础参数] [搜索起点] [参数选项]
说明:
[基础参数]
:这里指的是5个基础参数,必须放在路径前面。[搜索起点]
:指定要搜索的目录,可以是相对路径或绝对路径 。[参数选项]
:条件测试、逻辑操作和动作,find 命令的核心内容。
⚖️ 2.2 xargs用法
find [基础参数] [搜索起点] [参数选项] | xargs [命令参数]
说明:
[命令参数]
:这里的命令参数指的是 xargs 需要对找到的目标进行的操作
📦 2.3 默认用法
当 find 不添加任何参数单独执行的时候相当于以下命令
find -P . -print
即递归打印当前目录下的所有文件
🛠️ 3. find 参数
find 命令中出现的参数可分为6种
基础参数
:用于控制 find 命令本身的遍历方式或解析路径方式的参数。检测类参数
:用于检测文件是否满足条件的参数 。操作类参数
:用于对搜索到的文件进行进一步操作的参数。逻辑类参数
:用于组合多个检测逻辑或操作逻辑的参数。全局类参数
:用于改变 find 命令整体行为的参数 。局部类参数
:用于改变部分检测或操作行为的参数。
还有一类参数虽然不在 find 命令中出现,但是会影响到 find 行为,需要在执行 find 命令前通过 export 设置。
环境变量参数
:会影响到 find 行为的环境变量
🕋 3.1 基础参数
-P: 不添加基础参数的时候,find 的默认选项,即遍历目录时不跟随符号链接。
-L:遍历目录时跟随符号链接。效果:
解释:这里 find 遍历了 apk 符号链接所指向的目录,打印了目录内所有文件的文件名
⚠️ 注意:-L 参数只跟随目录的符号链接,即进入符号链接指向的目录并遍历目录中的内容。普通文件的符号链接不跟随,按照原样打印文件的名字。(效果图中 -L 参数没有打印符号链接 led 指向的 led.bin)
-H:遍历目录时若搜索起点直接是符号链接,则追踪它;否则像 -P 一样处理。效果:
解释:这里 . 表示当前目录,不是符号链接,当作搜索起点传给 find -H 不跟随;apk 是符号链接,当作搜索起点传给 find -H 就进行了跟随遍历。
⚠️ 注意:这里 -H 也是只跟随目录的符号链接,而不跟随普通文件的符号链接。
-D: find 的调试参数,用它可以查看 find 内部处理过程。先通过 find -D help 查看用法提示,然后根据提示输入参数。专门给源码狗准备的,效果:
-Olevel: 根据level优化搜索顺序,一般level=0-1用于小目录或简单搜索,level=2用于中型目录,level=3用于大型目录或复杂搜索。
🔎 3.2 检测类参数
-atime n: 搜索最后访问时间在 n 天前,但是在 n+1 天内的文件;如果 -atime 后面跟的是 +n ,就表示搜索最后访问时间在 n 天内的文件;如果 -atime 后面跟的是 -n ,就表示搜索最后访问时间在 n 天前的文件。这里官方给的解释很晦涩,只要记住最常用的 -atime 0 表示搜索在1天内访问过的文件就行了。
-ctime n: 搜索状态修改时间在 n 天前,但是在 n+1 天内的文件,+n 和 -n 的逻辑和 -atime 一致。
-mtime n: 搜索最后修改时间在 n 天前,但是在 n+1 天内的文件,+n 和 -n 的逻辑和 -atime 一致。效果:
解释:-mtime 0 参数只显示在24小时内修改过的文件
-anewer filename: 搜索最后访问时间比 filename 更迟(更新)的文件
-cnewer filename: 搜索状态修改时间比 filename 更迟(更新)的文件
-newer filename: 搜索最后修改时间比 filename 更迟(更新)的文件,效果:
解释:当前目录下只有 apk 的修改时间比 led 更迟,所以 find 只打印 apk
-type filetype: 搜索文件类型为 filetype 的文件,filetype 可以为 f(普通文件)、d(目录文件)、l(符号链接文件)、c(字符设备文件)、b(块设备文件)、P(有名管道文件)、s(套接字文件)中的任意一种。效果:
解释:这里由于添加了 -type d 参数,所以 find 只打印了当前目录下的目录文件(即使是隐藏文件也会打印)
-perm mode: 搜索文件权限符合指定 mode 的文件。效果:
解释:这里搜索权限为777(mode = 777)的文件,当前目录下就只有两个符号链接文件满足,所以打印两个符号链接文件
-uid n: 搜索属主的用户 ID(UID)为 n 的文件。
-gid n: 搜索属主的组 ID(GID)为 n 的文件。
-user username: 搜索属主用户名为 username 的文件,相当于 -uid 的更直观形式。效果:
解释:这里搜索所属者为 root 的文件,当前目录下没有,所以什么也不打印
-group groupname: 搜索属组名为 groupname 的文件,相当于 -gid 的更直观形式。
-size n: 搜索文件大小为 n 的文件。如果 -size 后面跟的是 +n 表示搜索文件大小大于 n 的文件;如果 -size 后面跟的是 -n 表示搜索文件大小小于 n 的文件。n 后面可以带单位:c(字节),k(KB),M(MB),G(GB)。效果:
解释:这里搜索文件大小大于10KB的文件,当前目录下只有 hello 满足条件,所以只打印 hello
-empty:搜索空文件或者空目录。
-links n:搜索硬链接数为 n 的文件。如果 -links 后面跟的是 +n 表示搜索硬连接数大于 n 的文件;如果 -links 后面跟的是 -n 表示搜索硬连接数小于 n 的文件。
-samefile filename:搜索和指定文件 filename 指向同一个 inode 的文件。基本上只能用来识别指向同一个inode的硬链接文件。
-name pattern: 搜索文件名符合 pattern 通配符匹配规则(大小写敏感)的文件。效果:
解释:这里搜索 .c 后缀的文件,当前目录下只有 hello.c 满足条件,所以只打印 hello.c 。
⚠️ 注意:pattern 通配符最好用”“包裹,否则容易报错。
-iname:搜索文件名符合 pattern 通配符匹配规则(大小写不敏感)的文件。
-path pattern: 搜索完整路径符合通配符匹配规则的文件。效果:
解释:这里搜索路径满足 ./h* 统配符的文件。当前目录下只有 hello 和 hello.c 满足,所以只打印 hello 和 hello.c
-regex pattern: 搜索完整路径符合正则表达式匹配规则的文件。和 -path 的区别是,这里可以用更复杂的正则语法。效果:
解释:这里搜索满足 ERE 正则表达式 ./[a-o]+ 的文件。当前目录下只有 led 和 hello 满足,所以只打印 led 和 hello
⚠️ 注意: find 命令的 -regex 参数默认只用的正则表达式为 Emacs 正则表达式,需要配合 -regextype 参数改成我们熟悉的 ERE 或 BRE 正则表达式。
-fstype typename:搜索属于某种文件系统类型的文件。效果:
解释:一般 Linux 系统下的文件都是属于 ext4 文件系统的,除非你用了特殊的文件系统做 Linux 或者挂载了 nfs 之类的网络文件系统,才会用到 -fstype 参数。
⚓️ 3.3 操作类参数
-print: 打印文件名,用换行符作为分隔符。是 find 命令的默认操作类参数,即如果不添加任何操作类参数,find 会自动帮你补上 -print。
-print0: 打印文件名,用 \0 作为分隔符。常用于和 xargs -0 搭配,防止文件名里有空格或换行导致解析错误,效果:
解释:效果大致就是这样的,说是\0在终端里不会显示,所以看不到。
-printf format: 按指定格式 format 输出结果,常见格式有 %p(路径)、%f(文件名)、%s(文件大小)。效果:
解释:这里输出格式中只打印了文件路径和文件大小,其他还有很多不常用的 format 格式,按需要添加,这里不一一列举
- -fprint filename: 将搜索结果写入 filename 文件,而不是打印到标准输出,用换行做分隔符。
- -fprint0 filename: 将搜索结果写入 filename 文件,而不是打印到标准输出,用 /0 做分隔符
- -fprintf filename formant: 将搜索结果按照指定格式 format 写入 filename 文件,而不是打印到标准输出。
- -ls: 将搜索到的结果以 ls -dils 的格式输出,显示文件的详细信息(包括 inode 、权限、大小、最后修改时间)。效果:
解释:ls -dils中的-dils表示:
-d: 显示目录本身而不是目录里的内容
-i: 显示文件的 inode 编号
-l: 按照 ls 的长格式显示详细信息
-s: 显示文件占用的磁盘块数
- -delete: 删除匹配到的文件。在删除过程中他会先删除子目录再删除父目录。
- -exec command {} \; : 对每个匹配的文件执行一次 command 。{} 会被替换为当前文件名,\;表示命令结束。效果:
解释:这里将搜索到的文件用 cat -n 命令打印内容并显示行数
⚠️ 注意:这里的command只适合简单命令,碰到复杂命令比如带重定向的或者带管道符号的命令建议用xargs。
- -exec command {} + : 用法和 -exec command {} \; 类似,但是效率比 -exec command {} \; 高。需要执行命令的目标文件较多时用这个命令。
- -execdir command {} \; :用法和 -exec command {} \; 类似,但是比 -exec command {} \; 更安全。可以防止文件名注入攻击和 TOCTOU攻击
解释:
文件名注入攻击,即使构建名为 "; rm -rf /" 或者 ";cat /etc/passwd" 的文件,放到搜索目录下,find 找到该文件在执行 -exec command {} \;的时候就会理解成2条命令从而误删文件或者泄漏密码。
TOCTOU (Time-of-Check to Time-of-Use) 攻击,即利用检查文件和使用文件的时间差进行的攻击,比如在 find 找到软连接 xxx 以后,再跳转到软连接所指向的目录之前,再这段时间内替换掉软连接让其指向攻击者指定的目录,这样 find 就会跳转到攻击者指定的目录执行 -exec command {} \;
- -execdir command {} + : 用法和 -exec command {} + 类似,但是比 -exec command {} + 更安全。可以防止文件名注入攻击和 TOCTOU 攻击
- -ok command {} \; : 用法和 -exec command {} \; 类似,但是会在每次执行命令前要求用户确认。
- -okdir command {} \; : 用法和 -execdir command {} \; 类似,但是会在每次执行命令前要求用户确认。
- -prune: 如果匹配到的文件时目录,则不进入该目录,常和 -path 和 -o 配合使用,用于排除目录。效果:
解释:这里利用 -path "./.vscode" -prune -o 排除了隐藏目录 .vscode,同时利用 -name "*.c" 匹配到了 hello.c 文件。-prune 的用法还有很多,但推荐就记这一种,不然容易乱。
- -quit: 在搜索过程中一旦找到一个匹配结果立即退出,不再继续搜索。效果:
解释:这里有两个文件满足条件,但是由于加了 -quit 参数所以在匹配到第一个的时候就退出了,第二个文件 hello.c 没有打印到终端上。
⛲️ 3.4 逻辑类参数
-not: 逻辑非,表示匹配不满足要求的文件。效果:
解释:这里利用 -not 匹配了当前目录下文件名中不含 hello 的文件
-and: 逻辑与,匹配同时满足多个要求的文件,效果:
解释: 这里利用 -and 匹配了文件名中含有 hello 并且文件大小大于10KB的文件。命令中通过转义的括号将 -and 包裹防止逻辑类参数的优先级问题。
⚠️ 注意:这里有逻辑短路,如果前面的条件不成立那么 find 不会继续匹配后面条件
-or: 逻辑或,匹配至少满足其中一个要求的文件,效果:
解释:这里利用 -or 匹配了文件大小大于10KB的或者修改时间在24小时内的文件。命令中通过转义的括号将 -or 包裹防止逻辑类参数的优先级问题。
⚠️ 注意:这里有逻辑短路,如果前面的条件成立那么 find 不会继续匹配后面条件
,: 按照顺序执行多个表达式,并丢弃第一个表达式的结果,效果:
解释:这里可以明显看到,最后的结果只显示了 -type l 参数匹配到的文件,而丢弃了 -name "hello*" 参数匹配到的文件。
🌐 3.5 全局类参数
-maxdepth n: 限制搜索的最大递归深度,最多只搜索到第 n 层子目录,效果:
解释:图中展示了最大递归深度为0、1、2时的状况
-mindepth n: 限制搜索的最小递归深度,最少需要搜索到第 n 层子目录,常配合 -maxdepth n 来显示搜索递归深度范围。
-mount: 用于限定搜索的范围,使得 find 命令只搜索当前目录所在的文件系统,不搜索其他文件系统。
-ignore_readdir_race: 防止搜索的时候文件被删除导致目录不一致而报错。常和 -delete 参数配合使用。
-noleaf: 取消硬连接数优化,在非 Linux 文件系统(比如 windows 的 exFAT )下使用 find 命令时需要添加这个参数
📍 3.6 局部类参数
-daystart: 将 -atime n、-ctime n、-mtime n 的计算时间标准从 n 个24小时改为 n 天前的00:00 。效果:
解释:这里 1.txt 在24小时内修改,但是今天没动过,所以加了 -daystart 以后没有匹配到
-regextype type: 将 find 用于匹配的正则表达式类型改成 type 类型,type 为 posix-basic 表示 BRE;type 为 posix-extended 表示 ERE。不加这个默认是 Emacs。
-warn: 开启 find 命令的警告信息
-nowarn: 关闭 find 命令的警告信息
🌏 3.7 环境变量参数
- PATH: 该环境变量会影响 -exec command {} \;、 -execdir command {} \; 执行命令的查找路径,简单来说如果 PATH 的路径里没有 command 的可执行文件,-exec command {} \; 的时候就会报错。
- LANG: 该环境变量会影响 find 处理中文或特殊字符文件名时的行为,如果文件命中有中文或特殊字符需要提前设置该变量,比如
export LANG=zh_CN.UTF-8
⚠️ 4. 注意事项
🧩 4.1 逻辑操作优先级问题
find 命令的逻辑类参数优先级较低,必须用转义的括号将逻辑包裹起来,否则会出现各种令人困惑的奇怪现象
🔤 4.2 文件名通配符展开问题
find 命令中用到的通配符需要加引号,否则被 shell 提前展开,进而导致无法预期的现象
🛡️ 4.3 执行命令安全问题
find 的 man 手册中明确指出了所有 find 可能包含的安全问题,包括文件名注入攻击和TOCTOU攻击,同时也指出即使是用了 -execdir command {} \; 也不能保证100%的安全,如果需要100% 的安全,推荐使用 -okdir command {} \; 参数。
解释:一般人这辈子都不一定会碰到黑客攻击,看个热闹就行了。
🔄 4.4 符号链接循环问题
find 命令在检测到符号链接循环的时候会报错,所以要确保你在跟随链接搜索的时候,搜索的目录中没有指向父目录的符号链接
📌 4.5 POSIX 与 GNU 区别
需要注意 find 的运行环境,本文中介绍的 find 为 GNU 的 find,如果是 POSIX 的 find 可能有些参数会不支持。
解释:一般主流Linux系统都是GNU,只有Android或BusyBox裁剪过的系统是POSIX
📕 5. 参考资料
find 命令官方 man 手册(POSIX / GNU):
https://man7.org/linux/man-pages/man1/find.1.html