目录

1. find

1.1 文件名 (-name)

1.2 文件类型 (-type)

1.3 文件大小 (-size)

1.4 目录层级

1.5 同时执行多个操作

1.5.1 exec

1.5.2 ok

1.5.3 xargs

2. grep

3.locate


在使用 Linux 系统的时候,我们经常会需要查找某些文件,但是大多数情况下我们并不能确定这些文件的具体位置,这样的话就非常浪费我们的时间。Linux 为我们提供了很多的用于文件搜索的命令,如果需求比较简单可以使用 locate,which,whereis 来完成搜索,如果需求复杂可以使用 find, grep 进行搜索。其中 which 在前边已经介绍过了, 使用方法和功能就直接略过了,whereis 局限性太大,不常用这里也就不介绍了。

1. find

find 是 Linux 中一个搜索功能非常强大的工具,它的主要功能是根据文件的属性,查找对应的磁盘文件,比如说我们常用的一些属性 文件名 , 文件类型 , 文件大小 , 文件的目录深度 等,下面基于这些常用数据来讲解一些具体的使用方法。

如果想用通过属性对文件进行搜索, 只需要指定出属性对应的参数就可以了, 下面将依次进行介绍。

1.1 文件名 (-name)

根据文件名进行搜索有两种方式: 精确查询和模糊查询。关于模糊查询必须要使用对应的通配符,最常用的有两个, 分别为 * 和 ?。其中 * 可以匹配零个或者多个字符, ?用于匹配单个字符。

如果我们进行模糊查询,建议(非必须)将带有通配符的文件名写到引号中(单引号或者双引号都可以),这样可以避免搜索命令执行失败(如果不加引号,某些情况下会这样)。

如果需要根据文件名进行搜索,需要使用参数 -name。

# 语法格式: 根据文件名搜索 
$ find 搜索的路径 -name 要搜索的文件名

根据文件名搜索举例:

# 模式搜索
# 搜索 root 家目录下文件后缀为 txt 的文件
[root@VM-8-14-centos ~]# find /root -name "*.txt"
/root/luffy/get/onepiece/haha.txt
/root/luffy/get/onepiece/onepiece.txt
/root/luffy/get/onepiece.txt
/root/luffy/get/link.txt
/root/luffy/robin.txt
/root/luffy/onepiece.txt
/root/ace/brother/finally/die.txt
/root/onepiece.txt

##################################################

# 精确搜索
# 搜索 root 家目录下文件名为 onepiece.txt 的文件
[root@VM-8-14-centos ~]# find /root -name "onepiece.txt"
/root/luffy/get/onepiece/onepiece.txt
/root/luffy/get/onepiece.txt
/root/luffy/onepiece.txt
/root/onepiece.txt

1.2 文件类型 (-type)

在前边文章中已经介绍过 Linux 中有 7 中文件类型 , 如果有去求我们可以通过 find 对指定类型的文件进行搜索,该属性对应的参数为 -type。其中每种类型都有对应的关键字,如下表:

文件类型类型的字符描述
普通文件类型f
目录类型d
软连接类型l
字符设备类型c
块设备类型b
管道类型p
本地套接字类型s
# 语法格式: 
$ find 搜索的路径 -type 文件类型

根据文件类型搜索举例:

# 搜索 root 用户家目录下, 软连接类型的文件
[root@VM-8-14-centos ~]# find /root -type l
/root/link.lnk
/root/luffy/get/link.lnk
/root/file/link

1.3 文件大小 (-size)

如果需要根据文件大小进行搜索,需要使用参数 -size。关于文件大小的单位有很多,可以根据实际需求选择,常用的分别有 k(小写), M(大写), G(大写)。

在进行文件大小判断的时候,需要指定相应的范围,涉及的符号有两个分别为:加号 (+) 和 减号 (-),下面具体说明其使用方法:

# 语法格式: 
$ find 搜索的路径 -size [+|-]文件大小
	- 文件大小需要加单位: 
		- k (小写)
		- M (大写)
		- G (大写)

关于文件大小的区间划分非常重要,请仔细阅读,并思考,可以自己画个图,这里以 4k 来举例:

  1. -size 4k 表示的区间为 (4-1k,4k], 表示一个区间,大于 3k, 小于等于 4k
  2. -size -4k: [0k, 4-1k], 表示一个区间,大于等于 0 并且 小于等于 3k
  3. -size +4k: (4k, 正无穷), 表示搜索大于 4k 的文件

根据文件大小搜索举例:

# 搜索当前目录下 大于1M的所有文件 (file>3M)
$ find ./ -size +3M

# 搜索当前目录下 大于等于0M并且小于等于2M的文件 (0M <= file <=2M)
$ find ./ -size -3M

# 搜索当前目录下 大于2M并且小于等于3M的文件 (2M < file <=3M)
$ find ./ -size 3M

# 搜索当前目录下 大于1M 并且 小于等于 3M 的文件
$ find ./ -size +1M -size -4M

1.4 目录层级

因为 Linux 的目录是树状结构,所有目录可能有很多层,在搜索某些属性的时候可以指定只搜索某几层目录,相关的参数有两个,分别为: -maxdepth 和 -mindepth。

这两个参数不能单独使用, 必须和其他属性一起使用,也就是搜索某几层目录中满足条件的文件。

  • -maxdepth: 最多搜索到第多少层目录,
  • -mindepth: 至少从第多少层开始搜索

下面通过 find 搜索某几层目录中文件名满足条件的文件:

# 查找文件, 从根目录开始, 最多搜索5层, 这个文件叫做 *.txt (1 <= 层数 <= 5)
$ sudo find / -maxdepth 5 -name "*.txt"

# 查找文件, 从根目录开始, 至少从第5层开始搜索, 这个文件叫做 *.txt (层数>=5层)
$ sudo find / -mindepth 5 -name "*.txt"

1.5 同时执行多个操作

在搜索文件的时候如果想在一个 find 执行多个操作,通过使用管道 (|) 的方式是行不通的,比如下面的操作:

# 比如: 通过find搜索最多两层目录中后缀为 .txt 的文件, 然后再查看这些满足条件的文件的详细信息
# 在find操作中直接通过管道操作多个指令, 最终输出的结果是有问题, 因此不能直接这样使用
$ find ./ -maxdepth 2  -name "*.txt" | ls -l
total 612
drwxr-xr-x 2 root root   4096 Jan 26 18:11 a
-rw-r--r-- 1 root root    269 Jan 26 17:44 a.c
drwxr-xr-x 3 root root   4096 Jan 26 18:39 ace
drwxr-xr-x 4 root root   4096 Jan 25 15:21 file
lrwxrwxrwx 1 root root     24 Jan 25 17:27 link.lnk -> /root/luffy/onepiece.txt
drwxr-xr-x 4 root root   4096 Jan 26 18:39 luffy
-r--r--r-- 1 root root     37 Jan 26 16:50 onepiece.txt
-rw-r--r-- 1 root root 598314 Dec  2 02:07 rarlinux-x64-6.0.0.tar.gz

如果想要实现上面的需求,需要在 find 中使用 exec, ok, xargs, 这样就可以在 find 命令执行完毕之后,再执行其他的子命令了。

1.5.1 exec

-exec 是 find 的参数,可以在exec参数后添加其他需要被执行的shell命令。

find 添加了 exec 参数之后,命令的尾部需要加一个后缀 {} \;, 注意 {} 和 \ 之间需要有一个空格。

在参数 -exec 后添加的 shell 命令处理的是 find 搜索之后的结果,find 的结果会作为 新添加的 shell 命令 的输入,最后在终端上输出最终的处理结果。

# 语法:
$ find 路径 参数 参数值 -exec shell命令2 {} \;

命令的使用效果演示:

# 搜索最多两层目录, 文件名后缀为 .txt的文件
$ find ./ -maxdepth 2  -name "*.txt" 
./luffy/robin.txt
./luffy/onepiece.txt
./onepiece.txt

# 搜索到满足条件的文件之后, 再继续查看文件的详细属性信息
$ find ./ -maxdepth 2  -name "*.txt" -exec ls -l {} \; 
-rw-r--r-- 1 root root 0 Jan 25 17:54 ./luffy/robin.txt
-r--r--r-- 2 root root 37 Jan 25 17:54 ./luffy/onepiece.txt
-r--r--r-- 1 root root 37 Jan 26 16:50 ./onepiece.txt

1.5.2 ok

-ok 和 -exec 都是 find 命令的参数,使用方式类似,但是这个参数是交互式的,在处理 find 的结果的时候,会向用户发起询问,比如在删除搜索结果的时候,为了保险起见,就需要询问机制了。

语法格式如下:

# 语法: 其实就是将 -exec 替换为 -ok, 其他都不变
$ find 路径 参数 参数值 -ok shell命令2 {} \;

命令效果演示:

# 搜索到了2个满足条件的文件
$ find ./ -maxdepth 1  -name "*.txt"
./aaaaa.txt 
./english.txt

# 查找并显示文件详细信息
$ find ./ -maxdepth 1  -name "*.txt" -ok ls -l {} \;     
< ls ... ./aaaaa.txt > ? y		# 同意显示文件详细信息
-rw-rw-r-- 1 robin robin 10 Apr 17 11:34 ./aaaaa.txt
< ls ... ./english.txt > ? n	# 不同意显示文件详细信息, 会跳过显示该条信息

# 什么时候需要交互呢? ---> 删除文件的时候
$ find ./ -maxdepth 1  -name "*.txt" -ok rm -rf {} \;     
< rm ... ./aaaaa.txt > ? y		# 同意删除
< rm ... ./english.txt > ? n	# 不同意删除

# 删除一个文件之后再次进行相同的搜索
$ find ./ -maxdepth 1  -name "*.txt"
./english.txt		# 只剩下了一个.txt 文件。

1.5.3 xargs

在使用 find 的 -exec 参数的时候,需要在指定的子命令尾部添加几个特殊字符 {} \;,一不小心就容易写错,有一种看起来更加直观、书写更加简便的方式,我们可以使用 xargs 替换掉 -exec 参数,而且在处理数据的时候 xargs更高效。有了 xargs 的加持我们就可以在 find 命令中直接使用管道完成前后命令的数据传递,使用方法如下:

# 在find 中 使用 xargs 关键字我们就可以使用管道了, 否则使用管道也不会起作用
# 将 find 搜索的结果通过管道传递给后边的shell命令继续处理
$ find 路径 参数 参数值 | xargs shell命令2

命令效果演示:

# 查找文件
$ find ./ -maxdepth 1  -name "*.cpp" 
./occi.cpp
./main.cpp
./test.cpp

# 查找文件, 并且显示文件的详细信息
robin@OS:~$ find ./ -maxdepth 1  -name "*.cpp" | xargs ls -l
-rw-r--r-- 1 robin robin 2223 Mar  2  2020 ./main.cpp
-rw-r--r-- 1 robin robin 1406 Mar  2  2020 ./occi.cpp
-rw-r--r-- 1 robin robin 2015 Mar  1  2020 ./test.cpp


# xargs的效率比使用 -exec 效率高
	-exec:  将find查询的结果逐条传递给后边的shell命令
	-xargs: 将find查询的结果一次性传递给后边的shell命令

2. grep

和 find 不同 grep 命令用于查找文件里符合条件的字符串。grep 命令中有几个常用参数,下面介绍一下:

  • -r: 如果需要搜索目录中的文件内容,需要进行递归操作,必须指定该参数
  • -i: 对应要搜索的关键字,忽略字符大小写的差别
  • -n: 在显示符合样式的那一行之前,标示出该行的列数编号
# 语法格式: 
$ grep "搜索的内容" 搜索的路径/文件 参数

对应要搜索的文件内容,建议放到引号中,因为关键字中可能有特殊字符,或者有空格,从而导致解析错误。

关于引号, 单双都可以,可根据自己的需求选择。

搜索举例:

# 搜索指定文件中是否有字符串 include
[root@VM-8-14-centos ~]# grep "include" a.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

# 不区分大小写进行搜索
[root@VM-8-14-centos ~]# grep "INCLUDE" a.c
[root@VM-8-14-centos ~]# grep "INCLUDE" a.c -i
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

# 搜索指定目录中哪些文件中包含字符串 include 并且显示关键字所在的行号
[root@VM-8-14-centos ~]# grep "include" ./ -rn        
./a.c:1:#include <stdio.h>
./a.c:2:#include <unistd.h>
./a.c:3:#include <fcntl.h>
./luffy/get/e.c:1:#include <stdio.h>
./luffy/get/e.c:2:#include <unistd.h>
./luffy/get/e.c:3:#include <fcntl.h>
./luffy/c.c:1:#include <stdio.h>
./luffy/c.c:2:#include <unistd.h>
./luffy/c.c:3:#include <fcntl.h>
./ace/b.c:1:#include <stdio.h>
./ace/b.c:2:#include <unistd.h>
./ace/b.c:3:#include <fcntl.h>
./.bash_history:1449:grep "include" ./
./.bash_history:1451:grep "include" ./ -r
./.bash_history:1465:grep "include" a.c

3.locate

我们可以将 locate 看作是一个简化版的 find, 使用这个命令我们可以根据文件名搜索本地的磁盘文件 , 但是 locate的效率比find要高很多。原因在于它不搜索具体目录,而是搜索一个本地的数据库文件,这个数据库中含有本地所有文件信息。Linux 系统自动创建这个数据库,并且每天自动更新一次,所以使用 locate 命令查不到最新变动过的文件。为了避免这种情况,可以在使用locate之前,先使用updatedb命令,手动更新数据库。

# 使用管理员权限更新本地数据库文件, root用户这样做
$ updatedb
# 非root用户需要加 sudo
$ sudo updatedb

locate 有一些常用参数,使用之前先来介绍一下:

搜索所有目录下以某个关键字开头的文件

$ locate test		# 搜索所有目录下以 test 开头的文件

搜索指定目录下以某个关键字开头的文件,指定的目录必须要使用绝对路径

$ locate /home/robin/test    # 指定搜索目录为 /home/robin/, 文件以 test 开头

搜索文件的时候,忽略文件名的大小写,使用参数 -i

$ locate TEST -i	# 文件名以小写的test为前缀的文件也能被搜索到

列出前 N 个匹配到的文件名称或路径名称,使用参数 -n

$ locate test -n 5		# 搜索文件前缀为 test 的文件, 并且只显示5条信息

基于正则表达式进行文件名匹配,查找符合条件的文件,使用参数 -r

# 使用该参数, 需要有正则表达式基础
$ locate -r "\.cpp$"		# 搜索以 .cpp 结尾的文件

正则表达式小科普:

  1. 在正则表达式中 . 可以匹配任意一个 非 \n 的单字符
  2. 上边的命令中使用转译字符 \ 对特殊字符. 转译,就得到了普通的字符.
  3. 在正则表达式中 $ 放到字符尾部,表示字符串必须以这个字符结尾,上边的命令中修饰的是字符 p
  4. 正则表达式中的 字符 c 和后边的字符 p 需要进行字节匹配,没有特殊含义
  5. 通过上面的解释就能明白 \.cpp$ 说的就是以 .cpp 结尾的字符串

作者: 苏丙榅
链接: https://subingwen.cn/linux/search/
来源: 爱编程的大丙
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Logo

更多推荐