#《快乐的 Linux 命令行》

#find

find命令接收一个或多个目录名来执行搜索。

1
2
$ find Documents/ | wc -l
17905

-type d限制了只搜索目录。

1
2
$ find Documents/ -type d | wc -l
1741

find命令支持的常见文件类型测试条件:

文件类型 描述
b 块特殊设备文件
c 字符特殊设备文件
d 目录
f 普通文件
l 符号链接

-name参数后面跟通配符来根据文件名查找,用双引号引起来,从而阻止shell展开路径名。

-size +1M代表文件大小大于1M的文件。

1
2
3
4
$ find Documents/ -type f -name "*.png" | wc -l
512
$ find Documents/ -type f -name "*.png" -size +1M | wc -l
55

-size的常见单位:

字符 单位
b 512个字节块[1]
c 字节
w 两个字节的字
k 千字节(1024个字节单位)
M 兆字节(1048576个字节单位)
G 千兆字节(1073741824个字节单位)
测试条件 描述
-cmin n 匹配内容或属性最后修改时间正好在n分钟之前的文件或目录,指定少于n分钟之前,使用-n,指定多于n分钟之前,使用+n
-cnewer file 匹配内容或属性最后修改时间晚于file的文件或目录
-ctime n 匹配内容和属性最后修改时间n*24小时之前的文件和目录
-empty 匹配空文件和目录
-group name 匹配属于一个组的文件或目录,组可以用组名组ID来表示
-iname pattern 就像-name测试条件,但是不区分大小写
-inum n 匹配inode号是n的文件[2]
-mmin n 匹配内容被修改n分钟之前的文件或目录
-mtime n 匹配的文件或目录的内容被修改n*24小时之前
-name pattern 用指定的通配符模式匹配的文件和目录
-newer file 匹配内容晚于指定的文件的文件和目录[3]
-nouser 匹配不属于一个有效用户的文件和目录[4]
-nogroup 匹配不属于一个有效的组的文件和目录
-perm mode 匹配权限已经设置为指定的mode的文件或目录,mode 可以用八进制符号表示法
-samefile name 类似于-inum测试条件,匹配和文件name享有同样inode号的文件
-size n 匹配大小n的文件
-type c 匹配文件类型c的文件
-user name 匹配属于某个用户的文件或目录,这个用户可以通过用户名用户ID来表示

多个测试条件的连接符号:

操作符 描述
-and 如果操作符两边的测试条件都是真,则匹配。可以简写为-a若没有使用操作符,则默认使用-and
-or 若操作符两边的任一个测试条件为真,则匹配。可以简写为-o
-or 若操作符两边的任一个测试条件为真,则匹配。可以简写为-o
-not 若操作符后面的测试条件是假,则匹配。可以简写为一个感叹号!
() 把测试条件和操作符组合起来形成更大的表达式

find命令允许基于搜索结果来执行操作:

操作 描述
-delete 删除当前匹配的文件
-ls 对匹配的文件执行等同的ls -dils命令
-print 把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是默认操作
-quit 一旦找到一个匹配,退出

find命令也可以调用任意命令:

1
-exec command {} ;

{}是当前路径名的符号表示。

因为花括号和分号对于shell有特殊含义,所以它们必须被引起来或被转义:

1
-exec rm '{}' ';'

通过使用-ok行为来代替-exec,在执行每个指定的命令之前,会提示用户。

通过把末尾的分号改为加号,就激活了find命令的一个功能,把搜索结果结合为一个参数列表,然后用于所期望的命令的一次执行。

1
2
3
find Documents/ -type f -name '*.bak' -exec ls -l '{}' ';'
# 替换为
find Documents/ -type f -name '*.bak' -exec ls -l '{}' '+'

#测试

1
2
mkdir -p playground/dir-{00{1..9},0{10..99},100}
touch playground/dir-{00{1..9},0{10..99},100}/file-{A..Z}
1
find playground -type f -name 'file-A'

不同于ls命令,find命令的输出结果是无序的,其顺序由存储设备的布局决定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ touch playground/timestamp
$ stat playground/timestamp
File: playground/timestamp
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 811h/2065d Inode: 106302168 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ hs) Gid: ( 1000/ hs)
Access: 2021-04-08 22:33:03.175222042 +0800
Modify: 2021-04-08 22:33:03.175222042 +0800
Change: 2021-04-08 22:33:03.175222042 +0800
Birth: -

# touch 命令会修改时间信息
$ touch playground/timestamp
$ stat playground/timestamp
File: playground/timestamp
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 811h/2065d Inode: 106302168 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ hs) Gid: ( 1000/ hs)
Access: 2021-04-08 22:36:02.113310036 +0800
Modify: 2021-04-08 22:36:02.113310036 +0800
Change: 2021-04-08 22:36:02.113310036 +0800
Birth: -
1
find playground -type f -name 'file-B' -exec touch '{}' ';'
1
find playground -type f -newer playground/timestamp
1
find playground \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \) | sort
1
find playground \( -type f -not -perm 0600 -exec chmod 0600 '{}' ';' \) -or \( -type d -not -perm 0711 -exec chmod 0700 '{}' ';' \)

  1. 如果没有指定单位,则这是默认值。 ↩︎

  2. 不懂啥意思,以后回来可以补上测试例子,翻译是这对于找到某个特殊 inode 的所有硬链接很有帮助↩︎

  3. 这在编写执行备份的shell脚本的时候很有帮助。每次你制作一个备份,更新文件(比如说日志),然后使用find命令来判断哪些文件自从上一次更新之后被更改了。 ↩︎

  4. 这可以用来查找属于被删除的帐户的文件或监测攻击行为 ↩︎