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
:小于n
n
:等于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 | # 有一些文件,文件名中带有空格 |