find 命令是 Linux 上查找文件的神器,要想用好命令行,find 是必须掌握的命令之一。

基本语法

find 命令的基本语法如下:

  1. find [path] [expression]

默认 path 为当前目录;默认 expression 是 -print ,因此单单的 find 命令,打印当前目录下的所有文件。
expression 可以包含的操作有:operators, options, tests, actions。接下来一一介绍。

operator

不指定的话,默认是 -and

EXPR1 -a EXPR2 或者 EXPR1 -and EXPR2
EXPR1 -o EXPR2 或者 EXPR1 -or EXPR2
! EXPR 或者 -not EXPR

option

在其它表达式之前使用,常用的有:

-maxdepth LEVELS 指定目录的最大深度
-mindepth LEVELS 指定目录的最小深度
-depth 从目录的最深层开始查找

test

下面的表达式,可以用来过滤文件或目录,判断是否匹配指定的操作。

参数 作用
-name PATTERN 匹配名称
-iname PATTERN 匹配名称忽略大小写
-empty 匹配空文件
-perm [-/]MODE 匹配权限
-user NAME 匹配文件的所有者
-group NAME 匹配文件的所属组
-gid N 匹配组 ID
-nouser 匹配无所有者的文件
-nogroup 匹配无所有组的文件
-atime N 匹配文件的访问时间(单位是天)(N 可以是 +N,可以是 -N,也可以是N)(access time)
-ctime N 匹配文件的状态改变时间(change time)
-mtime N 匹配文件的修改时间(modify time)
-amin N 匹配文件的访问时间(单位是分钟)
-cmin N 匹配文件的状态改变时间
-mmin N 匹配温婉的访问时间
-anewer FILE 匹配 access time 比 FILE 新的文件
-cnewer FILE 匹配 change time 比 FILE 新的文件
-newer FILE 匹配比文件 FILE 新的文件
-type b/c/d/p/f/l/s/D 匹配文件类型
-size N 匹配文件的大小
-path PATTERN 匹配指定路径,配合 -prune 参数排除指定目录
-executable 匹配可执行文件
-readable 匹配可读文件
-writable 匹配可写文件

action

action 可以指定对匹配文件的操作:

-delete 删除查找出来的文件
-print 打印查找出来的文件(为默认操作)
-exec COMMAND {} \; 使用命令进一步处理搜索结果(最后的 \ 是对 ; 转义)
-prune 如果是目录的话,不遍历
-ok 和 -exec 作用相同,但是在执行命令之前,会让用户确定是否执行

基本使用方法

默认情况下,find 命令会对文件夹进行递归查找。如果不指定目录,默认查找当前文件夹。

  1. # 列出当前目录以及子目录下的所有文件
  2. $ find
  3. # 等同于
  4. $ find .
  5. # 也等同于
  6. $ find . -print
  7. # 列出指定目录下的文件
  8. $ find ./test
  9. # 查找指定名称的文件
  10. $ find ./test -name "1.txt"
  11. # 如果不指定目录,find 默认查找当前文件夹
  12. $ find -name "1.txt"
  13. # 使用通配符匹配文件名称
  14. $ find ./test -name "*.txt"
  15. # 忽略大小写使用 iname
  16. $ find ./test -iname "*.txt"
  17. # 在多个目录中查找
  18. $ find ./test ./temp -name "*.txt"
  19. # 查找不是 txt 的文件
  20. $ find . -not -name "*.txt"
  21. # 等同于
  22. $ find . ! -name "*.txt"

使用路径匹配

  1. # 查找 controller 路径下 Sys 开头的文件
  2. $ find . -path "*/controller/*" -name "Sys*"
  3. # 不查找 C 目录
  4. $ find . ! -path "./C/*" -name "*.c"

使用多个条件查找

默认情况下,find 命令使用 AND 操作符连接多个条件,可以指定使用 OR 操作符。

  1. # 查找以 test 开头但不是 txt 的文件
  2. $ find . -name "test*" ! -name "*.txt"
  3. # 查找 txt 文件或者 java 文件
  4. $ find . -name "*.txt" -o -name "*.java"

使用圆括号可以将多个表达式结合在一起,但是圆括号在命令中有特殊的车含义,所以需要使用 \ 来进行转义。

忽略目录

  1. # 不遍历当前目录
  2. $ find . -prune
  3. .
  4. # 忽略 /data/temp 目录(当 -path "/data/temp" 为真时,执行 -prune,否则执行 -print)
  5. $ find /data -path "/data/temp" -prune -o -print
  6. # 忽略多个目录
  7. $ find /data \( -path /data/temp -o -path /data/test \) -prune -o -print
  8. # 相当于
  9. $ find /data -type d \( -name temp -o -name test \) -prune -o -print

限制查找的深度

  1. # 限制目录最大深度是 1,也就是当前文件夹
  2. $ find . -maxdepth 1 -name "*.txt"
  3. # 限制目录的最小深度是 2
  4. $ find . -mindepth 2 -name "*.txt"
  5. # 相当于 tree -L 1
  6. $ find . -maxdepth 1 -type d

使用指定类型的文件

使用 -type 可以指定要查找文件的类型。

  1. # 列出当前目录下的目录
  2. $ find -type d
  3. # 列出当前目录下的文件
  4. $ find -type f
  5. # 查找以 abc 开头的文件
  6. $ find ./test -type f -name "abc*"

其它查找方式

查找空文件和目录

  1. # 查找空文件
  2. $ find /tmp -type f -empty
  3. # 查找空目录
  4. $ find /tmp -type d -empty

基于文件大小查找

  1. # 查找指定大小的文件
  2. $ find . -size 50M
  3. # 查找大小在一定范围的文件
  4. $ find . -size +50M -size -100M

基于文件拥有者和用户组查找

  1. # 查找当前目录下,属于 flykyle 的文件
  2. $ find . -user flykyle
  3. # 查找当前目录下,属于 developer 用户组的文件
  4. $ find . -group developer
  5. # 查找没有对应任何用户的文件(文件的属主账号已经被删除)
  6. $ chown 555 test.txt
  7. $ find . -nouser
  8. # 查找没有对应任何组的文件
  9. $ chown .555 test.txt
  10. $ find . -nogroup

基于文件权限查找

  1. # 查找 777 权限的文件
  2. $ find . -type f -perm 0777
  3. # 查找不是 777 权限的文件
  4. $ find . -type f ! -perm 777
  5. # 查找权限是 551,并且设置了 Sticky Bit 的文件
  6. $ find . -perm 1551
  7. # 查找权限是 644,并且设置了 SGID Bit 的文件
  8. $ find / -perm 2644
  9. # 查找设置了 SUID 的文件
  10. $ find / -perm /u=s
  11. # 查找设置了 SGID 的文件
  12. $ find / -perm /g=s
  13. # 查找只读文件
  14. $ find / -perm /u=r
  15. # 查找可执行文件
  16. # 由于权限不足,某些目录会拒接访问。命令中的 2>/dev/null 正是用于清除输出中的错误访问结果
  17. $ find /bin -perm /a=x 2>/dev/null

基于时间和日期查找

  1. # 查找过去的第 3 天修改的文件
  2. $ find / -mtime 3
  3. # 查找指定时间范围内修改的文件
  4. $ find / -mtime +1 -mtime -7
  5. # 查找过去的 10 天内被访问过的文件
  6. $ find / -atime -10
  7. # 查找过去的 N 分钟内状态发生改变的文件
  8. $ find ./test -cmin -60
  9. # 查找过去的 1 小时内被修改过内容的文件
  10. $ find ./test -mmin -60
  11. # 查找过去的 1 小时内被访问过的文件
  12. $ find ./test -amin -60
  13. # 查找更改时间比 file1 新但比 file2 旧的文件
  14. $ find . -newer file1.txt ! -newer file2.txt

查找后进一步操作

-exec COMMAND {} \; 操作,其中的 {} 表示 find 命令搜索出的每一个文件。

  1. # 删除 temp 文件
  2. $ find . -name "*.temp" -delete
  3. # 相当于
  4. $ find . -name "*.temp" -exec rm -f {} \;
  5. # 列出文件的详细信息
  6. $ find . -name "*.txt" -ls
  7. # 相当于
  8. $ find . -name "*.txt" -exec ls -l {} \;
  9. # 相当于
  10. $ find . -name "*.txt" | xargs ls -l
  11. # 查找权限是 777 的目录,然后修改权限为 755
  12. $ find /home -type d -perm 777 -print -exec chmod 755 {} \;
  13. # 删除更改时间在 14 天前的日志,-ok 需要用户确认是否删除
  14. $ find /var/log -name "*.log" -mtime +14 -ok rm {} \;

配合 xargs 使用

xargs 是一个命令,可以向其它命令传递参数。

  1. # 列出 txt 文件
  2. $ find . -name "*.txt" | xargs ls -l
  3. # 删除文件
  4. # xargs 的 -p 选项会让用户确认是否执行后面的命来
  5. $ find . -name "*.temp" | xargs -p rm -f

xargs 和 exec 的区别

-exec xargs
将查找的结果文件名逐个传递给后面的命令执行,如果文件比较多,执行效率比较低 将查找的结果一次性传给后面的命令执行,执行效率高,可以使用 -n 参数控制一次传递文件的个数
文件名有空格等特殊字符也照常处理 文件名有空格,需要采用特殊的方式( find . -name "*.txt" -print0 | xargs -0 ls -lh
  1. # 可以看到 -exec 对每个文件都执行了 echo 命令
  2. $ find . -type f -exec echo file-is: {} \;
  3. file-is: ./test/2.java
  4. file-is: ./test/abc.java
  5. file-is: ./test/1.txt
  6. file-is: ./test/abc.txt
  7. file-is: ./test.tar.gz
  8. file-is: ./file1.txt
  9. file-is: ./file3.txt
  10. file-is: ./file2.txt
  11. file-is: ./xx/file10.txt
  12. file-is: ./temp/2.java
  13. # 可以看到使用 xargs,echo 命令只执行了一次
  14. $ find . -type f | xargs echo file-is:
  15. file-is: ./test/2.java ./test/abc.java ./test/1.txt ./test/abc.txt ./test.tar.gz ./file1.txt ./file3.txt ./file2.txt ./xx/file10.txt ./temp/2.java
  16. # 使用 -n 指定传递参数的个数
  17. $ find . -type f | xargs -n 3 echo file-is:
  18. file-is: ./test/2.java ./test/abc.java ./test/1.txt
  19. file-is: ./test/abc.txt ./test.tar.gz ./file1.txt
  20. file-is: ./file3.txt ./file2.txt ./xx/file10.txt
  21. file-is: ./temp/2.java
  22. # 创建一个名称带有空格的文件
  23. $ touch "xiao can.txt"
  24. # 使用 -exec 可以查看
  25. $ find . -name "xiao*" -exec ls -l {} \;
  26. -rw-r--r-- 1 xiaocan xiaocan 0 Dec 8 12:01 './xiao can.txt'
  27. # 正常使用 xargs 会报错
  28. $ find . -name "xiao*" | xargs ls -l
  29. ls: cannot access './xiao': No such file or directory
  30. ls: cannot access 'can.txt': No such file or directory
  31. # 特殊处理
  32. $ find . -name "xiao*" -print0 | xargs -0 ls -l
  33. -rw-r--r-- 1 xiaocan xiaocan 0 Dec 8 12:01 './xiao can.txt'

练习

  1. 把 /script 目录及其子目录下的 .sh 文件中的 “./service.log” 替换为 “./service-test.log”
  1. # 要用到的 sed 命令
  2. # sed -i "s#./service.log#./service-test.log#g"
  3. # find + exec 方法
  4. $ find /script -name "*.sh" -exec sed -i "s#./service.log#./service-test.log#g" {} \;
  5. # find + xargs 防止
  6. $ find /script -name "*.sh" -exec | xargs sed -i "s#./service.log#./service-test.log#g"
  7. # 使用反引号 ``(命令中如果有反引号,优先执行反引号中的命令)
  8. $ sed -i "s#./service.log#./service-test.log#g" `find /script -name "*.sh"`
  1. 删除一个目录下的所有文件,但保留指定文件
  1. # 创建10个文件
  2. $ touch file{1..10}.txt
  3. # 使用 find + xargs 删除文件
  4. $ find . -type f ! -name "file10.txt" | xargs rm -f
  5. # 使用 find + exec 删除
  6. $ find . -type f ! -name "file10.txt" -exec rm -f {} \;
  1. 将找到的文件移到指定的位置
  1. # 将 14 天的前的日志,移到 /var/oldlog 文件夹
  2. # 使用 xargs -i 参数,使得 {} 代表 find 查找到的文件
  3. $ find /var/log -name "*.log" -mtime +14 | xargs -i mv {} /var/oldlog
  4. # 或者
  5. # mv 的 -t 选项,可以颠倒源和目标
  6. $ find /var/log -name "*.log" -mtime +14 | xargs mv -t /var/oldlog
  7. # 使用反引号
  8. $ mv `find /var/log -name "*.log" -mtime +14` /var/oldlog

参考

http://man7.org/linux/man-pages/man1/find.1.html