【第3章】搜索命令
「按名字找文件」用 find/fd/locate,「按内容找文本」用 grep,「看目录结构」用 tree。理解它们的实时遍历 vs 索引查询差异,能少走很多弯路。
fd
fd - find 的现代替代品,语法简洁、默认智能过滤、速度快(Rust 实现)
常用命令
fd config在当前目录递归查找文件名含 config 的文件(模式默认是正则,且默认忽略 .gitignore 和隐藏文件)fd '\.log$'用正则匹配以 .log 结尾的文件fd -e md按扩展名查找,等价于 find 的-name "*.md"但更顺手fd -t d node只找目录(-t f文件、-t l符号链接、-t x可执行文件)fd -H -I pattern-H包含隐藏文件,-I不忽略 .gitignore(两个都加约等于裸 find)fd -e log -x rm {}对每个结果并行执行命令;-X把所有结果拼成一条命令执行fd -e jpg -x convert {} {.}.png{}完整路径、{.}去扩展名、{/}文件名、{//}父目录fd --changed-within 1d找 1 天内改动过的文件(也支持2h、30min、2026-07-01)fd -S +100m找大于 100M 的文件(-100k为小于 100k)fd -d 2 -t d限制递归深度为 2 层fd -E node_modules -E '*.min.js' TODO用-E追加排除模式fd -g '*.test.ts'用 glob 通配符而非正则匹配
语法格式
fd [OPTIONS] [pattern] [path...]
OPTIONS
-e ext按扩展名过滤(可多次指定)-t f|d|l|x|e按类型过滤:文件/目录/链接/可执行/空文件-H包含隐藏文件;-I(或-u)忽略 ignore 规则,-uu等于-H -I全都搜-E pattern排除匹配的文件/目录-d n最大递归深度-x cmd对每个结果并行执行命令;-X cmd批量执行一次-S [+-]size按大小过滤--changed-within / --changed-before按修改时间过滤-g使用 glob 模式;-F按字面字符串匹配(不解释正则)-p让 pattern 匹配完整路径而不仅是文件名-0NUL 分隔输出,配合xargs -0
备注
- 安装:macOS
brew install fd;Debian/Ubuntu 包名为fd-find,可执行文件叫fdfind,一般alias fd=fdfind - 默认忽略隐藏文件和 .gitignore 内容是最常见的「怎么搜不到」原因——搜不到先加
-HI试试 - pattern 是智能大小写:全小写时忽略大小写,含大写字母时精确匹配;结果默认彩色输出
find
find - 按名称、类型、时间、大小等条件在目录树中实时查找文件并可对结果执行操作
常用命令
find . -name "*.log"在当前目录递归查找所有 .log 文件(-iname忽略大小写)find /etc -type f -name "nginx.conf"只找普通文件;-type d找目录,-type l找符号链接find . -type f -mtime -7找 7 天内修改过的文件;-mtime +30为 30 天前修改的find . -type f -mmin -60找 60 分钟内修改过的文件(排查刚发生的问题很常用)find / -type f -size +100M找大于 100M 的文件(-size -1k小于 1k;单位 c/k/M/G)find /var/log -name "*.log" -mtime +30 -delete删除 30 天前的日志(先去掉-delete预览一遍再执行)find . -name "*.sh" -exec chmod +x {} \;对每个结果执行命令,{}是占位符find . -type f -name "*.tmp" -exec rm -f {} ++结尾把多个结果拼成一次命令执行,比\;快find . -type f -name "*.java" | xargs grep -l "TODO"结合 xargs 二次过滤(路径含空格时用-print0 | xargs -0)find . -maxdepth 1 -type d只看当前层的目录,不往下递归find . -type f -newer build.log找比某文件更新的文件find . -type f -user root -perm -4000按属主 + 权限位查找(如 setuid 文件)
语法格式
find [path...] [expression]
expression 由测试条件(-name、-type…)、操作(-print、-exec…)和逻辑运算符(-a 与、-o 或、! 非)组成,条件间默认是「与」。
OPTIONS
-name pattern按文件名通配符匹配(-iname忽略大小写)-type f|d|l按类型过滤:普通文件 / 目录 / 符号链接-mtime [+-]n按修改时间(天);-mmin按分钟;-atime/-ctime访问/状态变更时间-size [+-]n[ckMG]按大小过滤-maxdepth n/-mindepth n限制递归深度-exec cmd {} \;逐个执行命令;-exec cmd {} +批量执行-delete直接删除匹配结果(危险,务必先预览)-print0以 NUL 分隔输出,配合xargs -0处理含空格文件名-path pattern -prune跳过整棵子树,如find . -path ./node_modules -prune -o -name "*.js" -print-empty匹配空文件或空目录
备注
-mtime +7是「7 天以前」,-mtime -7是「7 天以内」,不带符号是「恰好第 7 天」,最容易记反- 用
-prune排除目录时,后面必须接-o ... -print,否则被剪掉的目录名本身也会打印出来 - macOS/BSD find 语法基本兼容,但
find不写路径时 GNU 默认当前目录、BSD 会报错,稳妥起见永远写上.;日常交互式查找可用更快更简洁的 fd 替代
grep
grep - 按正则表达式在文件或标准输入中搜索匹配的行
常用命令
grep "error" app.log在文件中搜索包含 error 的行grep -i "error" app.log忽略大小写匹配grep -rn "TODO" src/递归搜索目录并显示文件名和行号(排查代码最常用)grep -v "DEBUG" app.log反向匹配,输出不含 DEBUG 的行(常用于过滤噪音)grep -E "ERROR|WARN" app.log扩展正则,匹配多个关键词之一(等价旧写法 egrep)grep -o 'trace_id=[0-9a-f]*' app.log只输出匹配到的片段本身而非整行,适合提取字段grep -C 3 "Exception" app.log显示匹配行上下各 3 行上下文(-B只看前、-A只看后)grep -l "main" *.c只列出含匹配的文件名(-L列不含匹配的文件)grep -c "GET /api" access.log统计匹配行数grep -w "port" nginx.conf全词匹配,避免命中 export、report 等ps aux | grep -F "[java]"-F按字面字符串匹配,不解释正则元字符grep -rn --include='*.py' "import requests" .递归时只搜指定类型的文件(--exclude-dir=.git排除目录)
语法格式
grep [OPTIONS] PATTERN [FILE...]
不指定 FILE 时从标准输入读取,因此常接在管道后面使用。
OPTIONS
-r/-R递归搜索目录(-R会跟随符号链接)-i忽略大小写-v反向匹配(输出不匹配的行)-n显示行号-E扩展正则(支持|、+、?、{}不加反斜杠);-PPerl 正则(GNU 版支持\d、断言等)-o只输出匹配的部分,每个匹配一行-A n/-B n/-C n显示匹配行之后 / 之前 / 前后 n 行上下文-l/-L只列出(不)含匹配的文件名-c统计每个文件的匹配行数-w全词匹配;-x整行匹配-F按固定字符串匹配(不解释正则)--include/--exclude/--exclude-dir递归时按通配符筛选文件或排除目录
备注
- macOS 自带 BSD grep:不支持
-P(Perl 正则),且-r细节略有差异;需要 GNU 行为可brew install grep后用ggrep - 大代码仓库里交互式搜索建议用 ripgrep(
rg):默认递归、自动忽略 .gitignore 和二进制文件、速度远快于 grep,常用参数(-i/-v/-o/-C/-l)与 grep 基本一致 - 搜索
-开头的内容会被当成选项,需写成grep -- "-foo" file或grep -e "-foo" file
locate
locate - 基于预建数据库按文件名瞬间查找文件(快但可能不是最新)
常用命令
locate nginx.conf在整个文件系统数据库中查找路径含 nginx.conf 的条目locate -i readme忽略大小写查找locate -b '\nginx.conf'只匹配文件名本身(basename),\前缀表示精确名而非子串locate -c '*.log'只统计匹配数量,不列路径locate -e '*.pid'只输出当前真实存在的文件(剔除数据库里已被删除的陈旧条目)locate -r '/etc/.*\.conf$'用正则匹配完整路径locate -l 20 '*.mp4'只显示前 20 条结果sudo updatedb手动重建数据库(新建的文件立刻可被 locate 找到)locate -S查看数据库统计信息(条目数、更新时间等,plocate/mlocate 支持)
语法格式
locate [OPTIONS] pattern...
pattern 不含通配符时按「子串」匹配路径;含 *? 等通配符时按 glob 匹配整个路径。
OPTIONS
-i忽略大小写-b只匹配文件名部分,不匹配目录路径-c只输出匹配条目数-e只输出实际仍存在的文件-r regexp使用基本正则匹配(--regex为扩展正则)-l N限制输出条数-0以 NUL 分隔输出,配合xargs -0-A多个 pattern 时要求全部匹配(默认任一匹配即输出)
备注
- 数据库靠 cron/systemd timer 定期跑
updatedb(通常每天一次),刚创建/删除的文件查不到或有幽灵条目是正常现象,急用先sudo updatedb - 现代发行版多用 plocate(索引更小、查询更快)替代 mlocate,命令行为兼容;权限上只返回当前用户有权访问的路径
- macOS 自带的是 BSD locate,需先
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist启用数据库;日常在 macOS 上更推荐mdfind -name xxx(Spotlight 实时索引)
tree
tree - 以树状结构递归列出目录内容
常用命令
tree以树状图列出当前目录下所有文件和子目录tree -L 2只展开到第 2 层,快速看清目录骨架tree -d只显示目录,不列文件tree -a连同隐藏文件(以.开头)一起显示tree -I 'node_modules|.git|dist'排除匹配模式的目录/文件(多个模式用|分隔)tree -P '*.md'只列出匹配通配符的文件(常配合--prune去掉空目录)tree -h同时显示文件大小(人类可读单位,如 4.0K、1.2M)tree --du -h目录行显示该目录累计占用大小,类似dutree -f显示每个条目的完整相对路径前缀tree -t按修改时间排序(配合-r反序,最新的在最后/最前)tree -L 3 -o struct.txt把结果输出到文件,便于贴进文档tree -J/tree -X以 JSON / XML 格式输出,便于程序解析
语法格式
tree [OPTIONS] [directory ...]
OPTIONS
-L level限制递归显示的目录深度-d只列目录-a显示隐藏文件-I pattern排除匹配 pattern 的条目(|分隔多个)-P pattern只显示匹配 pattern 的文件--prune修剪掉不含匹配文件的空目录(配合-P)-h/-s人类可读大小 / 字节数-f显示完整路径前缀-C强制彩色输出(管道/重定向时默认关闭)--gitignore遵循 .gitignore 规则过滤(1.9+ 版本支持)-o filename输出到指定文件
备注
- macOS 不自带 tree,需
brew install tree;Linux 发行版一般也要手动安装(apt/yum install tree) - 大目录直接
tree输出会刷屏,习惯先加-L 2或-I排除node_modules、.git等 - 中文/特殊字符显示为转义时,可加
-N原样输出,或确认终端 locale 为 UTF-8