find
find 是一个在 Linux 和类 Unix 系统中用于在文件系统中搜索文件和目录的强大命令行工具。它能够根据各种条件(如名称、大小、类型、修改时间、权限、所有者等)进行搜索,并对找到的文件执行指定的操作。
find 的核心作用是:
- 递归搜索:从指定目录开始,递归地遍历其所有子目录,查找符合条件的文件和目录。
- 多条件组合:支持使用逻辑运算符(AND, OR, NOT)组合多个搜索条件。
- 执行操作:对找到的文件执行命令(如删除、复制、移动、修改权限等)。
基本语法
1 | find [路径] [表达式] |
**
[路径]**:指定从哪个目录开始搜索。可以是一个或多个目录。如果省略,则默认为当前目录 (.)。**
[表达式]**:由选项、测试、动作和操作符组成,用于定义搜索条件和对找到的文件执行的操作。
如果路径是相对径,输出就是相对路径;如果是绝对路径,输出就是绝对路径。
find 的常用选项和用法示例
1. 按名称或模式搜索
-name <pattern>:按文件名(或目录名)搜索,支持通配符(*,?,[])。区分大小写。1
2find . -name "*.txt" # 在当前目录及其子目录中查找所有以 .txt 结尾的文件
find /home/user -name "report_???.pdf" # 查找 report_001.pdf, report_abc.pdf 等-iname <pattern>:按文件名(或目录名)搜索,不区分大小写。1
find . -iname "document.pdf" # 查找 document.pdf, Document.pdf, DOCUMENT.PDF 等
2. 按类型搜索
**
-type <type>**:按文件类型搜索。f:普通文件 (file)d:目录 (directory)l:符号链接 (symbolic link)b:块设备 (block device)c:字符设备 (character device)p:命名管道 (named pipe / FIFO)s:socket
1
2find /var/log -type f -name "*.log" # 查找 /var/log 下所有的日志文件
find . -type d # 查找当前目录下的所有子目录
3. 按大小搜索
**
-size <n[cwbkMG]>**:按文件大小搜索。n是整数,后缀表示单位:c:字节 (bytes)w:双字 (2 bytes)b:块 (512-byte blocks,默认单位)k:千字节 (kilobytes)M:兆字节 (megabytes)G:千兆字节 (gigabytes)+n:大于n-n:小于nn:等于n(通常不精确,因为文件大小不总是精确匹配)
1
2find /tmp -size +10M # 查找 /tmp 下所有大于 10MB 的文件
find /var/log -size -1k # 查找 /var/log 下所有小于 1KB 的文件
4. 按时间搜索
**
-atime <n>**:按文件最后访问时间(access time)搜索,n天前。**
-mtime <n>**:按文件最后修改时间(modification time)搜索,n天前。**
-ctime <n>**:按文件状态最后改变时间(change time,包括权限、所有者、修改时间等)搜索,n天前。+n:大于n天前-n:小于n天前 (即n天以内)n:恰好n天前
1
2find . -mtime +7 # 查找 7 天前修改的文件
find /tmp -atime -1 # 查找 1 天内(今天)被访问过的文件-amin,-mmin,-cmin:与上述类似,但以分钟为单位。1
find . -mmin -60 # 查找 60 分钟内修改过的文件
**
-newer <file>**:查找比指定文件更新(修改时间更晚)的文件。1
find . -newer my_template.txt # 查找比 my_template.txt 更新的文件
5. 按权限或所有者搜索
find -perm 用于根据权限模式查找文件。它的语法是:
1 | find <路径> -perm <模式> |
<模式> 是一个三位或四位的八进制数(如 644、755),或使用符号模式(如 u=rwx)。
这里的关键是理解 <模式> 前面的修饰符,它们决定了匹配的逻辑。
| 修饰符 | 含义 | 查找逻辑 |
|---|---|---|
| 无修饰符 | 精确匹配 | 权限必须完全等于给定的模式。例如,755 模式只会匹配权限恰好为 rwxr-xr-x 的文件。 |
- | 权限包含(“至少满足”) | 文件的权限必须包含给定的模式。例如,-755 会匹配 775 和 755,因为它们的权限都包含 755。 |
/ | 任一用户权限匹配(“或”) | 文件的权限中,只要任一用户(所有者、组、其他人)的权限位与模式匹配,就符合条件。 |
1. 无修饰符:精确匹配模式
这是最严格的查找方式。文件权限必须与你指定的模式完全相同。
- 实例:查找当前目录下权限为
644(rw-r--r--)的文件。
1 | find . -type f -perm 644 |
如果某个文件的权限是 744,它就不会被找到,因为它比 644 多了执行权限。
2. - 修饰符:至少满足模式
这是最常用、最实用的方式。它查找权限至少是给定模式的文件。这就像一个“包含”的关系。
- 实例 1:查找当前目录下所有至少拥有执行权限的文件(对于所有者、组、其他人任一)。
1 | find . -perm -111 |
这里的 111 模式表示:所有者有执行权限(--x),所属组有执行权限(--x),其他人有执行权限(--x)。-111 意味着只要文件的权限中包含了这些权限,就会被匹配。例如,权限为 755 的文件会被匹配,因为它包含 111。
- 实例 2:查找当前目录下,所有者拥有读写执行权限,所属组和其他人至少拥有读执行权限的文件。
1 | find . -perm -755 |
-755 意味着文件的权限必须包含 rwxr-xr-x。例如,权限为 775、757 甚至 777 的文件都会被匹配,因为它们都“包含” 755 的所有权限位。
3. / 修饰符:任一用户权限匹配模式
这个修饰符的逻辑是:只要任一用户(所有者、组或其他)的权限位与给定的模式相匹配,就符合条件。这就像一个“或”的关系。
- 实例 1:查找当前目录下,所有者、所属组或其他人中,只要有一个拥有写权限的文件。
1 | find . -perm /222 |
222 模式表示所有者有写权限(-w-)、所属组有写权限(-w-)、其他人有写权限(-w-)。/222 表示只要满足其中任意一个条件(/ 意味着“或”),文件就会被找到。例如,644(所有者有写)、464(所属组有写)和 446(其他人有写)都会被匹配。
- 实例 2:查找所有者拥有写权限,或所属组拥有执行权限的文件。
1 | find . -perm /210 |
210 模式表示:所有者有写权限(2),所属组有执行权限(1),其他人无任何权限(0)。/210 的含义是:只要所有者的权限位中包含 w 或所属组的权限位中包含 x,就匹配。
实例 1:查找并修复不安全的权限
假设你需要找到并修复那些权限过于宽松(例如,对所有人开放写权限)的文件。
1 | # 查找对"其他人"开放写权限的文件 |
找到后,你可以结合 xargs 或 -exec 来批量修改权限。
1 | # 找到后批量修改权限为 644 |
实例 2:查找缺少执行权限的脚本
假设你的服务器上有很多脚本,但由于权限设置错误,一些脚本无法执行。你可以快速找出它们。
1 | # 查找所有者、组或其他任何人都没有执行权限的脚本 |
! -perm /111 的含义是,反向查找那些不包含任一执行权限的文件。
实例 3:查找并删除无用文件
你发现一些日志文件权限设置不正确,导致应用程序无法写入。你想找到这些文件并删除。
1 | # 查找对所有者、组、其他人都无写权限的日志文件 |
find -perm 命令的精髓在于修饰符。
- 无修饰符:精确匹配。
-:权限包含,最常用。用于查找权限至少满足某些条件的文件。/:任一用户权限匹配。用于查找任一用户满足某些条件的文件。
熟练掌握这三个修饰符,你就能像一个权限管理专家一样,轻松地在复杂的文件系统中定位和管理文件,这是每一位 Linux 运维工程师必备的技能。
**
-perm <mode>**:按文件权限搜索。mode:精确匹配权限(例如644)。-mode:至少拥有指定权限(例如-755意味着必须有 755 或更高,如 777)。/mode:任意拥有指定权限位中的一个(例如/222意味着文件对所有者、组或其他用户中至少有一个拥有写权限)。
1
2find . -perm 644 # 查找权限为 644 的文件
find . -perm -o+w # 查找其他用户有写权限的文件**
-user <name>**:按文件所有者搜索。1
find /var/www -user apache # 查找 apache 用户拥有的文件
**
-group <name>**:按文件所属组搜索。1
find /var/www -group developers # 查找 developers 组拥有的文件
6. 组合条件和逻辑操作符
find 默认的组合方式是 AND(逻辑与),也可以使用显式的操作符:
-a或不写:AND(默认)-o:OR(逻辑或)!:NOT(逻辑非,通常需要加\转义)():用于分组,通常需要加\转义以避免 shell 解释。
1 | # 查找名字以 .log 结尾,且大小大于 1M 的文件 |
7. 对找到的文件执行操作
**
-print**:打印找到的文件名。这是默认动作,但显式写出有助于理解。1
find . -name "*.bak" -print
**
-exec <command> {} \;**:对每个找到的文件执行指定的命令。{}:代表当前找到的文件名。;:命令结束符,需要用\转义。- 注意:对于大量文件,这可能会很慢,因为它为每个文件启动一个新进程。
1
2find . -name "*.tmp" -exec rm {} \; # 删除所有 .tmp 文件
find . -name "*.log" -exec gzip {} \; # 压缩所有 .log 文件**
-exec <command> {} +**:类似于-exec ... \;,但更高效。它会将多个找到的文件名作为参数一次性传递给命令,而不是每个文件启动一个进程。当文件数量很多时,推荐使用此方式。但要注意command是否支持多参数,否则报错。1
find . -name "*.txt" -exec ls -l {} + # 列出所有 .txt 文件的详细信息
**
-delete**:直接删除找到的文件。比rm更安全,因为find不会因为文件类型不对而删除目录,也不会因为权限问题而停止。1
find /tmp -type f -name "*.old" -delete # 删除 /tmp 下所有名为 *.old 的普通文件
- **
-ls**:以ls -dils命令的格式打印找到的文件信息。这包括 inode 号、块数、权限、硬链接数、所有者、组、大小、修改时间以及文件名。提供比-print更详细的信息,但不如-printf灵活自定义。
1 | find . -name "*.log" -ls # 列出所有 .log 文件的详细列表信息 |
-ok <command> {} \;:与-exec ... \;类似,但在执行每个命令之前会询问用户确认。提供一个交互式的安全网,在执行潜在危险操作(如删除)时非常有用。
1 | find . -name "*.bak" -ok rm {} \; # 在删除每个 .bak 文件前会提示 rm ... ? y/n |
只列出路径或文件名
find输出匹配文件的完整路径时,未指定任何其他动作,如果只列出路径,或者只列出文件名,find 可以通过结合不同的选项和动作来实现。
只列出路径:
当你执行 find . -name "*.txt" 时,它默认就是输出文件的相对路径(如果从当前目录开始搜)或绝对路径(如果你指定了绝对路径)。
1 | # 在当前目录及其子目录中查找所有 .txt 文件,并显示它们的相对路径。 |
如果你希望输出绝对路径,可以使用 find 结合 realpath 或 pwd -P:
1 | # 使用 -exec 配合 realpath 获取绝对路径 |
只列出文件名(不含路径):
如果你只想要文件名,而不要前面的路径部分,可以使用 basename 命令来处理 find 的输出。
1 | # 对每个找到的 .txt 文件,只打印其文件名部分 |
find . -name "*.txt":这部分是标准的find命令,用于查找所有.txt文件。-exec basename {} \;:-exec:表示对每个找到的文件执行一个命令。basename {}:basename命令会从给定的路径中提取出文件名(或目录名)部分。{}是find传递给basename命令的当前文件的占位符。;:是-exec命令的结束符,需要用反斜杠\进行转义,以防止 shell 解释它。
要只列出文件名而不带路径,find 最强大的内置工具是 -printf 动作。它允许你以自定义的格式打印输出,类似于 C 语言的 printf 函数。
-printf 提供了许多格式化指令,其中:
%f: 打印找到的文件或目录的文件名(basename)。
1 | # 在当前目录及其子目录中查找所有以 .txt 结尾的文件,并只打印其文件名 |
-printf 的优势:
- 效率更高:与
-exec basename {} \;相比,-printf是find的内置动作,它不需要为每个文件启动一个新的basename进程,因此在处理大量文件时会显著更快。 - 格式化能力强:除了
%f,-printf还有其他有用的格式化指令,例如:%p:完整路径名(与-print类似)%h:目录名(dirname)%s:文件大小(字节)%t:修改时间%u:所有者用户名%m:权限(八进制)- 等等…
列出文件名和大小
1 | # 查找 .txt 文件并列出文件名和大小 |
-print0
作用:与
-print类似,都是打印找到的文件名。但-print0不会在每个文件名后添加一个换行符,而是添加一个ASCII NULL 字符(\0)。在 Linux 文件系统中,文件名可以包含几乎任何字符,除了
/和 NULL 字符。这意味着文件名中可以有空格、换行符、制表符等。- 传统的
-print会用换行符分隔文件名。如果文件名本身包含换行符,那么一个文件名就会被误识别为多个文件。 - 如果文件名包含空格,当你把
find -print的输出通过管道传递给xargs等命令时,xargs默认会把空格作为分隔符,这会导致文件名被错误地分割,从而引发错误或意外操作。 - NULL 字符保证了每个文件名都能被准确地识别,不会因内部的空格或换行符而被分割。
- 传统的
使用:
-print0几乎总是与能够处理 NULL 分隔输入的命令(如xargs -0或read -d $'\0')结合使用。
删除包含空格的文件名:
假设你有一个名为 my document.txt 的文件,你想删除它。
错误的方式(会失败或出错):
1
2find . -name "my document.txt" -print | xargs rm
# 这里的 xargs 可能会尝试删除 'my' 和 'document.txt' 两个文件,导致错误。正确的方式(推荐使用
-print0和xargs -0):1
2find . -name "my document.txt" -print0 | xargs -0 rm
# 这样 xargs 会正确地将 "my document.txt" 作为一个完整的参数传递给 rm。
find . -name "my document.txt" -print0:find找到文件后,输出my document.txt\0(一个字符串,后面跟着一个 NULL 字符)。|:将find的输出通过管道传递。xargs -0 rm:xargs接收到 NULL 分隔的输入,并知道如何将my document.txt作为一个单一的参数传递给rm命令。应用场景:
处理带有空格或特殊字符的文件名:这是其主要用途,确保文件名在管道中传递时不会被错误解析。
与
xargs -0结合:这是最常见的搭配,用于对find找到的文件执行各种命令,例如rm、mv、cp、grep等。与
read -d $'\0'结合:在 Bash 脚本中,如果你需要逐个处理find -print0的输出,可以使用read -d $'\0'命令。1
2
3
4
5
6
7
while IFS= read -r -d $'\0' file; do
echo "Processing file: '$file'"
# 这里可以对文件进行其他操作,例如:
# cp "$file" /backup/
done < <(find . -type f -name "*.log" -print0)IFS=和read -r:防止read命令对文件名中的空格和反斜杠进行额外的解释。-d $'\0':指定 NULL 字符作为分隔符。< <(...):这是一个进程替换 (process substitution),允许while循环从find命令的输出中读取。
掌握 -print0 和 xargs -0 的组合是 Linux 命令行操作中一个非常重要的最佳实践,能帮助你编写更健壮、更不容易出错的脚本。
结合sh -c / bash -c 使用:
比如重命名文件
1 | # 有一些文件,文件名中带有空格 |
