Posts find 命令使用简介
Post
Cancel

find 命令使用简介

find 命令是 Linux 上查找文件的神器,要想用好命令行,find 是必须掌握的命令之一。使用 find --help 查看它的使用方法,下面做简单介绍。

基本语法

find 命令的基本语法如下:

1
find [path] [expression]

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

operator

有三种逻辑操作,与、或、非。不指定的话,默认是与操作,即 -and

  1. 与:EXPR1 -a EXPR2 或者 EXPR1 -and EXPR2
  2. 或:EXPR1 -o EXPR2 或者 EXPR1 -or EXPR2
  3. 非:! EXPR 或者 -not EXPR

option

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

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

test

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

参数作用
-name PATTERN匹配名称
-iname PATTERN匹配名称忽略大小写
-path PATTERN匹配指定路径,配合 -prune 参数排除指定目录
-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匹配文件的大小
-executable匹配可执行文件
-readable匹配可读文件
-writable匹配可写文件

action

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

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

基本使用方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 列出当前目录以及子目录下的所有文件
$ find
# 等同于
$ find .
# 也等同于
$ find . -print

# 列出指定目录下的文件
$ find ./test

# 查找指定名称的文件
$ find ./test -name "1.txt"

# 如果不指定目录,find 默认查找当前文件夹
$ find -name "1.txt"

# 使用通配符匹配文件名称
$ find ./test -name "*.txt"

# 忽略大小写使用 iname
$ find ./test -iname "*.txt"

# 在多个目录中查找
$ find ./test ./temp -name "*.txt"

# 查找不是 txt 的文件
$ find . -not -name "*.txt"
# 等同于
$ find . ! -name "*.txt"

使用路径匹配

1
2
3
4
5
# 查找 controller 路径下 Sys 开头的文件
$ find . -path "*/controller/*" -name "Sys*"

# 不查找 C 目录
$ find . ! -path "./C/*" -name "*.c"

使用多个条件查找

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

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

# 查找 txt 文件或者 java 文件
$ find . -name "*.txt" -o -name "*.java"

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

忽略目录

1
2
3
4
5
6
7
8
9
10
11
# 不遍历当前目录
$ find . -prune
.

# 忽略 /data/temp 目录(当 -path "/data/temp" 为真时,执行 -prune,否则执行 -print)
$ find /data -path "/data/temp" -prune -o -print

# 忽略多个目录
$ find /data \( -path /data/temp -o -path /data/test \) -prune -o -print
# 相当于
$ find /data -type d \( -name temp -o -name test \) -prune -o -print

限制查找的深度

1
2
3
4
5
6
7
8
9
# 限制目录最大深度是 1,也就是当前文件夹
$ find . -maxdepth 1 -name "*.txt"

# 限制目录的最小深度是 2
$ find . -mindepth 2 -name "*.txt"

# 相当于 tree -L 1
$ find . -maxdepth 1 -type d

使用指定类型的文件

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

1
2
3
4
5
6
7
8
9
# 列出当前目录下的目录
$ find -type d

# 列出当前目录下的文件
$ find -type f

# 查找以 abc 开头的文件
$ find ./test -type f -name "abc*"

查找空文件和目录

1
2
3
4
5
6
# 查找空文件
$ find /tmp -type f -empty

# 查找空目录
$ find /tmp -type d -empty

基于文件大小查找

1
2
3
4
5
6
# 查找指定大小的文件
$ find . -size 50M

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查找当前目录下,属于 flykyle 的文件
$ find . -user flykyle

# 查找当前目录下,属于 developer 用户组的文件
$ find . -group developer

# 查找没有对应任何用户的文件(文件的属主账号已经被删除)
$ chown 555 test.txt
$ find . -nouser

# 查找没有对应任何组的文件
$ chown .555 test.txt
$ find . -nogroup

基于文件权限查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查找 777 权限的文件
$ find . -type f -perm 0777

# 查找不是 777 权限的文件
$ find . -type f ! -perm 777

# 查找权限是 551,并且设置了 Sticky Bit 的文件
$ find . -perm 1551

# 查找权限是 644,并且设置了 SGID Bit 的文件
$ find / -perm 2644

# 查找设置了 SUID 的文件
$ find / -perm /u=s

# 查找设置了 SGID 的文件
$ find / -perm /g=s

# 查找只读文件
$ find / -perm /u=r

# 查找可执行文件
# 由于权限不足,某些目录会拒接访问。命令中的 2>/dev/null 正是用于清除输出中的错误访问结果
$ find /bin -perm /a=x 2>/dev/null

基于时间和日期查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查找过去的第 3 天修改的文件
$ find / -mtime 3

# 查找指定时间范围内修改的文件
$ find / -mtime +1 -mtime -7

# 查找过去的 10 天内被访问过的文件
$ find / -atime -10

# 查找过去的 N 分钟内状态发生改变的文件
$ find ./test -cmin -60

# 查找过去的 1 小时内被修改过内容的文件
$ find ./test -mmin -60

# 查找过去的 1 小时内被访问过的文件
$ find ./test -amin -60

# 查找更改时间比 file1 新但比 file2 旧的文件
$ find . -newer file1.txt ! -newer file2.txt

查找后进一步操作

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 删除 temp 文件
$ find . -name "*.temp" -delete
# 相当于
$ find . -name "*.temp" -exec rm -f {} \;

# 列出文件的详细信息
$ find . -name "*.txt" -ls
# 相当于
$ find . -name "*.txt" -exec ls -l {} \;
# 相当于
$ find . -name "*.txt" | xargs ls -l

# 查找权限是 777 的目录,然后修改权限为 755
$ find /home -type d -perm 777 -print -exec chmod 755 {} \;

# 删除更改时间在 14 天前的日志,-ok 需要用户确认是否删除 
$ find /var/log -name "*.log" -mtime +14 -ok rm {} \;

配合 xargs 使用

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

1
2
3
4
5
6
7
# 列出 txt 文件
$ find . -name "*.txt" | xargs ls -l

# 删除文件
# xargs 的 -p 选项会让用户确认是否执行后面的命来
$ find . -name "*.temp" | xargs -p rm -f

xargs 和 exec 的区别

-execxargs
将查找的结果文件名逐个传递给后面的命令执行,如果文件比较多,执行效率比较低将查找的结果一次性传给后面的命令执行,执行效率高,可以使用 -n 参数控制一次传递文件的个数
文件名有空格等特殊字符也照常处理文件名有空格,需要采用特殊的方式(find . -name “*.txt” -print0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 可以看到 -exec 对每个文件都执行了 echo 命令
$ find . -type f -exec echo file-is: {} \;
file-is: ./test/2.java
file-is: ./test/abc.java
file-is: ./test/1.txt
file-is: ./test/abc.txt
file-is: ./test.tar.gz
file-is: ./file1.txt
file-is: ./file3.txt
file-is: ./file2.txt
file-is: ./xx/file10.txt
file-is: ./temp/2.java

# 可以看到使用 xargs,echo 命令只执行了一次
$ find . -type f | xargs echo file-is:
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

# 使用 -n 指定传递参数的个数
$ find . -type f | xargs -n 3 echo file-is:
file-is: ./test/2.java ./test/abc.java ./test/1.txt
file-is: ./test/abc.txt ./test.tar.gz ./file1.txt
file-is: ./file3.txt ./file2.txt ./xx/file10.txt
file-is: ./temp/2.java

# 创建一个名称带有空格的文件
$ touch "xiao can.txt"
# 使用 -exec 可以查看
$ find . -name "xiao*" -exec ls -l {} \;
-rw-r--r-- 1 xiaocan xiaocan 0 Dec  8 12:01 './xiao can.txt'

# 正常使用 xargs 会报错
$ find . -name "xiao*" | xargs ls -l
ls: cannot access './xiao': No such file or directory
ls: cannot access 'can.txt': No such file or directory

# 特殊处理
$ find . -name "xiao*" -print0 | xargs -0 ls -l
-rw-r--r-- 1 xiaocan xiaocan 0 Dec  8 12:01 './xiao can.txt'

练习

  1. 把 /script 目录及其子目录下的 .sh 文件中的 “./service.log” 替换为 “./service-test.log”
1
2
3
4
5
6
7
8
9
10
11
12
# 要用到的 sed 命令
# sed -i "s#./service.log#./service-test.log#g"

# find + exec 方法
$ find /script -name "*.sh" -exec sed -i "s#./service.log#./service-test.log#g" {} \;

# find + xargs 防止
$ find /script -name "*.sh" -exec | xargs sed -i "s#./service.log#./service-test.log#g"

# 使用反引号 ``(命令中如果有反引号,优先执行反引号中的命令)
$ sed -i "s#./service.log#./service-test.log#g" `find /script -name "*.sh"`

  1. 删除一个目录下的所有文件,但保留指定文件
1
2
3
4
5
6
7
8
# 创建10个文件
$ touch file{1..10}.txt

# 使用 find + xargs 删除文件
$ find . -type f ! -name "file10.txt" | xargs rm -f

# 使用 find + exec 删除
$ find . -type f ! -name "file10.txt" -exec rm -f {} \;
  1. 将找到的文件移到指定的位置
1
2
3
4
5
6
7
8
9
10
11
# 将 14 天的前的日志,移到 /var/oldlog 文件夹
# 使用 xargs -i 参数,使得 {} 代表 find 查找到的文件
$ find /var/log -name "*.log" -mtime +14 | xargs -i mv {} /var/oldlog

# 或者
# mv 的 -t 选项,可以颠倒源和目标
$ find /var/log -name "*.log" -mtime +14 | xargs mv -t /var/oldlog

# 使用反引号
$ mv `find /var/log -name "*.log" -mtime +14` /var/oldlog

参考

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