目录

find 命令

find – walk a file hierarchy

在 Linux 命令中,find 是比较复杂难用的命令。

关于 find 命令的说明,也可以查看 GNU find 的在线帮助手册

在 Linux 中,目录也属于文件,find 在查找时,把目录也当成文件处理,会查找并处理目录名,并不是只处理文件名。

1
2
find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]

查找指定目录下的所有文件

1
2
3
ls
# Makefile.am  src  tests
find
1
2
3
4
5
6
.
./src
./src/main.c
./tests
./tests/bre.tests
./Makefile.am
1
find .

在 Linux 下,点号 ‘.’ 对应当前目录,所以 find . 就是查找当前目录下的所有文件,当没有提供目录参数时,默认就是使用 ‘.’ 这个参数。

find src 命令指定只查找 src 这个目录下的所有文件。

find src tests 命令指定查找 src、tests 这两个目录下的所有文件,可以同时提供多个目录名来指定查找这些目录。

find src tests 命令也可以写为 find ./src ./tests

如果在 find 命令后面提供文件名,则只在当前目录下查找该文件,不会在子目录下查找

1
2
3
4
find Makefile.am
# Makefile.am
find main.c
# find: `main.c': No such file or directory

匹配特定模式的文件名 -name

如果想要只匹配文件名,不包含目录路径部分,可以使用 -name pattern 表达式。

1
2
3
4
5
6
# 匹配单个后缀名
find /usr/local/doc -name '*.txt'
# 匹配多个后缀名
find . -name '*.c' -o -name '*.am'
# 打印出指定后缀的文件名本身、以及完整路径的文件名
find . \( -name '*.c' -o -name '*.am' \) -printf "%f  \t%p\n"

匹配特定类型的文件 -type

在 Linux 中,文件类型可以分为目录 (directory)、文本文件 (regular file)、符号链接 (symbolic link)、socket,等等。

  • d: directory
  • f: regular file
  • l: symbolic link
  • s: socket
1
2
find . -type f
find . -type d -name "log*"

指定时间戳查找 -atime mtime ctime

要按指定的时间戳搜索文件,Linux 系统中的 3 个不同的时间戳:

  • 访问时间戳 (atime):最后一次读取文件的时间。
  • 修改时间戳 (mtime):文件内容最后一次被修改的时间。
  • 更改时间戳 (ctime):上次更改文件元数据的时间(如,所有权、位置、文件类型和权限设置)
1
2
3
4
5
6
# 要搜索访问时间超过一年的文件
find . -type f -atime +365
# 查找修改时间正好是 5 天前的文件
find . -type f -mtime 5
# + 表示“大于”,- 表示“小于”。
find . -type f -ctime +5 -ctime -10

按照大小查找 -size

-size 选项使我们能够按指定大小查找文件。我们可以将其计量单位指定为以下约定:

  • b:512 字节块(默认)
  • c:字节
  • w:双字节字
  • k:KB
  • M:MB
  • G:GB
1
find . -type f -size +10M -size -1G

按照权限查找 -perm

合理控制文件的权限是 Linux 管理员的一项重要任务。find 命令的 -perm 选项可以帮助我们按指定权限查找文件

1
find . -type f -perm 777

777 权限的文件,意味着一个文件对其持有者、组和所有用户具有所有的读、写和可执行权限。

按所有权查找 -user

使用 -user 选项指定用户名。

1
find -type f -user ynthm

在找到文件后执行命令 -exec

在大多数情况下,我们希望在找到我们需要的文件后进行后续操作。例如将其删除,或检查它们的详细信息等等。-exec 命令使这些所有事情变得更加容易。

1
find . -type f -atime +365 -exec rm -rf {} \;

上述命令在 -exec 选项后是 rm -rf,其用于删除文件。{} 是用于查找结果的占位符。

注意:占位符 {} 非常重要,尤其是在您想删除文件时。因为,如果您不使用它,该命令将对所有文件执行(而不是您刚刚通过 find 命令找到的文件)。

1
2
find . -type f -atime +5 -exec ls {} \;
find . -type f -atime +5 -exec ls \;

-exec 选项后面的命令必须以分号 ; 结束。众所周知,转义字符用于去除单个字符的特殊含义。在 Linux 中,反斜杠 \ 用作转义字符。所以我们将它用于分号字符。

查找时指定忽略一个目录

如果想查找当前目录下的文件,且忽略 tests 目录

1
find . -path ./tests -prune -o -print
1
2
3
4
.
./src
./src/main.c
./Makefile.am

不同的表达式之间要用操作符分隔开,如果没有提供操作符,默认使用 -and 操作符。

所以 find . -path ./tests -prune -o -print 命令的完整格式其实是 find . -path ./tests -and -prune -o -print

-path pattern

这是一个 test 类型表达式,当 find 命令查找到的文件名完全匹配所给的 pattern 模式时,该表达式返回 true。

这里面最关键的点是,要完全匹配 find 命令查找到的名称,而不是部分匹配,也不是匹配文件的绝对路径名。

查看上面 find . 命令打印的信息,可以看到该命令打印的 tests 目录名是 ./tests,-path 参数要求是完全匹配才会返回 true,所以基于打印结果,就是要写为 -path ./tests 才会返回 true。-path tests 就是 false。

没有提供除了 -prune 表达式之外的其他 action 类型表达式时,默认会对所有返回 true 的文件名执行 -print 表达式,打印该文件名。

类似的,执行 find tests 命令,打印的 tests 目录名是 tests,那么 find tests -path tests 命令可以完全匹配 tests 模式,打印出这个目录名。而 find tests -path ./tests 就匹配不到,没有打印。

总的来说,在 -path 后面跟着的目录名,需要完全匹配 find 命令打印的目录名,而不是部分匹配。如果不确定 find 命令打印的目录名是什么,可以先不加 -path 参数执行一次 find 命令,看打印的文件名是什么,再把对应的文件名写到 -path 参数后面。

忽略多个目录的写法

如果想要忽略多个目录,要使用 -o 操作符把多个 -path pattern 表达式组合起来。

1
find . \( -path ./tests -o -path ./src \) -prune -o -print

正则表达式匹配完整路径文件名

在 find 命令里面,-path pattern 表达式和 -name pattern 表达式都是使用通配符来匹配模式,如果想要用正则表达式进行匹配,可以使用 -regex expr 表达式。

1
2
# 正则表达式来匹配多个后缀名
find . -regex '.*\.\(c\|am\)'

实用用法

如果你的 Linux 服务器上有一个名为 logs 的目录,如何删除该目录下最后一次访问时间超过一年的日志文件呢?

1
find . -type f -atime +365 -exec rm -rf {} \;