一、核心命令对比
| 命令 | 主要用途 | 搜索范围 | 是否依赖数据库 | 支持通配符/正则 | 执行速度 | 典型使用场景 |
|---|---|---|---|---|---|---|
which |
查找可执行命令路径 | $PATH 环境变量中的目录 |
否 | 否(仅精确匹配) | 极快 | 确认命令是否可用及具体路径 |
whereis |
查找二进制、源码、手册页位置 | 预定义系统目录(非 $PATH) |
否 | 否 | 快 | 快速获取程序相关文件位置 |
locate |
快速模糊查找文件名 | 全系统(基于 updatedb 数据库) |
是 | 支持通配符 | 极快 | 忘记确切路径但记得部分文件名 |
find |
强大灵活的实时文件搜索 | 指定目录树(默认递归) | 否 | 支持通配符与正则 | 较慢 | 精确条件搜索(如按时间、权限、类型等) |
注意 :
locate的数据库通常由cron每日更新(如/etc/cron.daily/mlocate),新创建的文件需手动运行sudo updatedb才能被索引。
二、各命令详解与示例
1. which
-
作用 :在
$PATH中查找可执行文件。 -
特点 :只返回第一个匹配项(除非加
-a显示所有)。 -
示例 :
bashwhich python3 # 输出:/usr/bin/python3 which -a gcc # 显示所有名为 gcc 的可执行文件路径
2. whereis
-
作用:查找二进制文件、源代码和 man 手册的位置。
-
特点 :不搜索
$PATH,而是使用硬编码的系统路径。 -
示例 :
bashwhereis nginx # 输出:nginx: /usr/sbin/nginx /usr/share/man/man8/nginx.8.gz
3. locate
-
作用:通过预建数据库快速查找文件名(支持模糊匹配)。
-
依赖包 :通常为
mlocate或plocate。 -
示例 :
bashlocate docker-compose.yml # 返回所有匹配文件路径 locate -i "*.LOG" # 忽略大小写查找 .LOG 文件
4. find
-
作用:在指定目录中实时遍历并按条件筛选文件。
-
功能最全:支持按名称、类型、大小、修改时间、权限、属主等过滤。
-
示例 :
bash# 查找当前目录下所有 .log 文件 find . -name "*.log" # 查找 7 天内修改过的 .conf 文件 find /etc -name "*.conf" -mtime -7 # 删除空目录 find /tmp -type d -empty -delete
5. 关于whereis查询不使用本地数据库的证明
证明1
shell
root@root:~# touch /usr/local/unique_test_file_xyz123
root@root:~# updatedb
/usr/bin/find: '/run/user/128/doc': Permission denied
/usr/bin/find: '/run/user/1000/gvfs': Permission denied
/usr/bin/find: '/run/user/1000/doc': Permission denied
root@root:~#
root@root:~#
root@root:~#
root@root:~# locate unique_test_file_xyz123
/usr/local/unique_test_file_xyz123
root@root:~# whereis unique_test_file_xyz123
unique_test_file_xyz123: /usr/local/unique_test_file_xyz123
root@root:~# rm /usr/local/unique_test_file_xyz123
root@root:~# locate unique_test_file_xyz123
/usr/local/unique_test_file_xyz123
root@root:~# whereis unique_test_file_xyz123
unique_test_file_xyz123:
观察到的现象
- 文件存在时:locate 和 whereis 都能找到
- 删除文件后:locate 仍能找到(数据库未更新),但 whereis 找不到
证明2
shell
root@root:~# cat whereis.sh
#!/bin/bash
echo "=== 验证 whereis 是否实时检查文件 ==="
# 创建测试文件
TEST_FILE="/usr/local/verify_$$.txt"
sudo touch "$TEST_FILE"
sudo updatedb
echo "1. 文件存在时:"
echo " locate: $(locate verify_$$.txt | head -1)"
echo " whereis: $(whereis verify_$$.txt | awk '{print $2}')"
# 使用 strace 查看系统调用
echo -e "\n2. 跟踪 whereis 的系统调用:"
strace -e trace=file whereis verify_$$.txt 2>&1 | tail -10
# 删除文件
sudo rm "$TEST_FILE"
echo -e "\n3. 文件删除后:"
echo " locate(数据库未更新): $(locate verify_$$.txt | head -1)"
echo " whereis(实时检查): $(whereis verify_$$.txt | awk '{print $2}')"
echo -e "\n4. 再次跟踪 whereis:"
strace -e trace=file whereis verify_$$.txt 2>&1 | tail -10
~
~
#输出结果
root@root:~# bash whereis.sh
=== 验证 whereis 是否实时检查文件 ===
/usr/bin/find: '/run/user/128/doc': Permission denied
/usr/bin/find: '/run/user/1000/gvfs': Permission denied
/usr/bin/find: '/run/user/1000/doc': Permission denied
1. 文件存在时:
locate: /usr/local/verify_482343.txt
whereis: /usr/local/verify_482343.txt
2. 跟踪 whereis 的系统调用:
openat(AT_FDCWD, "/usr/src/linux-hwe-6.8-headers-6.8.0-88", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/linux-hwe-6.8-headers-6.8.0-90", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/python3.10", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/linux-headers-6.8.0-90-generic", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
verify_482343.txt: /usr/local/verify_482343.txt
+++ exited with 0 +++
3. 文件删除后:
locate(数据库未更新): /usr/local/verify_482343.txt
whereis(实时检查):
4. 再次跟踪 whereis:
openat(AT_FDCWD, "/usr/src/linux-hwe-6.8-headers-6.8.0-88", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/linux-hwe-6.8-headers-6.8.0-90", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/python3.10", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/usr/src/linux-headers-6.8.0-90-generic", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
verify_482343.txt:
+++ exited with 0 +++
whereis 的工作方式:
- 不使用 locate 数据库(从 strace 看,它确实没有打开 /var/cache/locate/locatedb)
- 在预定义的硬编码目录列表中查找
- 对每个可能的路径进行实时 stat() 检查
预定义目录包括:
二进制目录:/bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin 等
手册页目录:/usr/share/man, /usr/local/man 等
源代码目录:/usr/src, /usr/local/src 等
证明3
shell
root@root:~# cat whereis_1.sh
#!/bin/bash
echo "=== 测试 whereis 的默认搜索模式 ==="
# 查看 whereis 默认搜索什么
man whereis | grep -A5 "DESCRIPTION"
# 测试不同选项
echo -e "\n1. 默认搜索(二进制+手册+源码):"
whereis ls
echo -e "\n2. 只搜索二进制文件:"
whereis -b ls
echo -e "\n3. 只搜索手册页:"
whereis -m ls
echo -e "\n4. 只搜索源代码:"
whereis -s ls
# 创建测试文件测试
echo -e "\n5. 测试新文件:"
TEST_FILE="/usr/local/testcmd_$$"
sudo touch "$TEST_FILE"
sudo chmod +x "$TEST_FILE"
sudo updatedb
echo " 默认搜索: $(whereis testcmd_$$)"
echo " 只搜二进制: $(whereis -b testcmd_$$)"
# 输出结果
root@root:~# bash whereis_1.sh
=== 测试 whereis 的默认搜索模式 ===
DESCRIPTION
whereis locates the binary, source and manual files for the specified command names. The
supplied names are first stripped of leading pathname components. Prefixes of s. resulting
from use of source code control are also dealt with. whereis then attempts to locate the
desired program in the standard Linux places, and in the places specified by $PATH and
$MANPATH.
1. 默认搜索(二进制+手册+源码):
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz
2. 只搜索二进制文件:
ls: /usr/bin/ls
3. 只搜索手册页:
ls: /usr/share/man/man1/ls.1.gz
4. 只搜索源代码:
ls:
5. 测试新文件:
/usr/bin/find: '/run/user/128/doc': Permission denied
/usr/bin/find: '/run/user/1000/gvfs': Permission denied
/usr/bin/find: '/run/user/1000/doc': Permission denied
默认搜索: testcmd_483961: /usr/local/testcmd_483961
只搜二进制: testcmd_483961: /usr/local/testcmd_483961
whereis 的官方描述中的关键点
- 明确说明查找二进制、源代码和手册文件
- 在标准 Linux 位置查找
- 也使用 PATH 和 MANPATH 环境变量
whereis 的实际工作机制
bash
# whereis 的搜索逻辑简化版
1. 解析命令行选项 (-b, -m, -s)
2. 确定要搜索的目录列表:
- 二进制目录:$PATH 中的目录 + 标准目录 (/bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, ...)
- 手册页目录:$MANPATH 中的目录 + 标准目录 (/usr/share/man, /usr/local/man, ...)
- 源代码目录:标准目录 (/usr/src, /usr/local/src, ...)
3. 在这些目录中递归搜索指定文件名
4. 对找到的文件进行类型判断和分类
5. 输出结果
与 locate 的根本区别
| 特性 | whereis |
locate |
|---|---|---|
| 数据源 | 实时搜索预定义目录 | 预构建的完整文件系统数据库 |
| 搜索范围 | 有限的标准目录 | 整个文件系统(排除的除外) |
| 实时性 | 实时 | 非实时(依赖数据库更新) |
| 用途 | 查找命令相关文件 | 查找任何文件 |
| 性能 | 较快(搜索有限目录) | 很快(数据库查询) |
证明 whereis 不使用数据库的关键证据
- 即时性 :
whereis在文件创建后立即能找到(虽然运行了updatedb,但即使不运行,如果文件在标准目录中,whereis也能找到) - 有限范围 :
whereis只能找到标准位置的文件 - 手册页明确说明:在"标准 Linux 位置"查找
最终结论
whereis 的本质:
- 不是数据库搜索工具 ,而是目录扫描工具
- 在预定义的标准目录集合中搜索
- 使用 PATH 和 MANPATH 环境变量扩展搜索范围
- 进行实时文件系统访问,不是查询数据库
三、其他相关命令
| 命令 | 说明 |
|---|---|
type |
Shell 内置命令,判断命令类型(别名、函数、内置、外部程序)。示例:type ls → ls is aliased to 'ls --color=auto' |
command |
跳过别名和函数,直接调用可执行文件。示例:command ls(绕过别名) |
whatis |
显示命令的简短描述(来自 man 页面的第一行)。示例:whatis curl → curl (1) - transfer a URL |
apropos |
按关键词搜索 man 手册页(等价于 man -k)。示例:apropos "copy file" |
四、使用决策建议
| 场景 | 推荐命令 |
|---|---|
| "这个命令在哪?" | which(确认是否在 PATH 中)或 type(更准确识别类型) |
| "这个软件有哪些相关文件?" | whereis |
| "我记得文件名里有 'config',但不知道在哪" | locate config(确保数据库已更新) |
| "找最近 1 小时修改的日志文件" | find /var/log -name "*.log" -mmin -60 |
| "找大于 1GB 的文件" | find / -type f -size +1G |
| "查某个命令是别名还是真实程序" | type command_name |
五、总结
- 速度优先 → 用
locate(但注意数据时效性)。 - 精确控制 → 用
find(功能最强,但较慢)。 - 查可执行命令 → 优先
which或type。 - 查程序全家桶(bin/man/src) →
whereis。