Linux下find和grep命令实战手册:从入门到进阶
Linux下查找文件find和grep命令的实战手册说实话在Linux服务器上待久了你会发现日常操作里最高频的需求其实就两个找文件和找文件里的内容。前者是find的活后者是grep的活。听起来简单但我在工作中见过太多人要么只会一个find / -name xxx要么一个grep -r打天下遇到稍微复杂点的场景就懵了。这篇东西的定位很明确——不是帮你背参数手册而是把find和grep这两个命令从入门到进阶掰开揉碎讲一遍包括它们背后的逻辑、常见的坑、以及我在真实项目中总结出来的一套组合打法。不管你是刚接触Linux的新手还是已经写了两三年脚本的运维、后端开发这篇文章都值得花十分钟过一遍有些经验是用时间换来的。1. 认识Linux查找利器find和grep到底解决什么问题1.1 两个命令的分工完全不同很多新手搞不清find和grep的区别其实一句话就能说透find是按“文件的属性”找文件比如文件名、大小、修改时间、权限grep是按“文件的内容”找文件比如某个文本里有没有包含一段关键字。为了让你记得更牢我打个比方。把文件系统想象成一个巨大的图书馆里面堆了上百万本书find相当于图书馆的检索系统。你说“我要找书名里带《Linux》的书”它给你列出来在哪个书架的哪个位置。它关心的是书的“外在特征”。grep相当于你拿着一本关键词清单亲自去翻每一本书的正文看哪一页出现了“网络协议”这四个字。它关心的是书的“内容”。这两个命令单独用就已经很能打了组合起来更是威力无穷。典型场景就是你记得项目里有个配置文件里面有timeout这个参数但你不记得文件名了。这时候grep -r timeout /etc/直接扫内容一次命中。反过来你知道日志文件是按天切割的想找三天前的那个文件去分析find /var/log -name *.log -mtime 2一条命令搞定。1.2 什么场景该用谁一张对照表我用一张表来快速对照方便你判断当前需求该用哪个命令需求场景首选命令典型写法说明按文件名查找findfind /data -name *.conf支持通配符模糊匹配按文件大小筛选findfind / -size 100M找大文件、清理磁盘时很好用按修改时间筛选findfind /logs -mtime 7配合定时任务清理过期日志按权限/属主筛选findfind / -user root -perm 644排查安全问题时有奇效在文件内容里搜关键字grepgrep -r ERROR /var/log扫目录下所有文件的文本内容过滤某个命令的输出grepps -ef | grep tomcat管道配合几乎人人都会用统计关键字出现次数grepgrep -c exception app.log快速判断报错频率一句话总结涉及“文件本身长什么样”用find涉及“文件里面写了什么”用grep。后面我会分别把这两个工具讲透再讲怎么组合。2. find命令深度实操从基础语法到高阶用法2.1 find的基本语法与最常用条件find命令的通用语法长这样find [要搜索的路径] [匹配条件] [处理动作]路径就是告诉find从哪里开始找这个不用多说。关键是匹配条件这是find的核心灵魂。最基础也最常用的就是-name按文件名精确匹配# 在当前目录下找所有 .log 结尾的文件 find . -name *.log # 在 /etc 目录下找文件名包含 nginx 的文件 find /etc -name *nginx* # 忽略大小写查找 find /data -iname README*这里有个细节值得注意-name后面的通配符*和?最好用双引号包起来避免在带空格的路径里出问题也可以防止shell先把通配符展开掉导致结果莫名其妙。按类型过滤用-type常用的参数有f普通文件、d目录、l符号链接# 找出所有目录 find /var -type d # 找出所有普通文件 find /opt -type f # 找出所有软链接 find /usr/bin -type l按大小过滤用-size支持k、M、G单位也支持和-表示大于、小于# 找出 /var/log 下大于100MB的文件 find /var/log -size 100M # 找出大小为0的空文件 find /tmp -size 0“找大文件清理磁盘”是我在服务器上干得最多的操作之一。排查磁盘告警时一条find / -xdev -size 1G -exec ls -lh {} \;能直接定位到罪魁祸首这个组合后面会详细讲。2.2 按时间、权限与属主过滤-mtime是另一个高频参数按文件的修改时间过滤。它的逻辑一开始容易把人绕晕我直接给你记忆口诀-mtime n表示“n天之前改的”-mtime -n表示“n天之内改的”-mtime n表示“刚好第n天改的”。# 找7天前修改过的文件 find /backup -mtime 7 # 找24小时内修改过的文件 find /var/www -mtime -1 # 按更细粒度找10分钟内修改过的文件 find /var/log -mmin -10权限和属主过滤在排查安全问题时特别有用。比如你想知道系统里哪些文件被设置了SUID位一种特殊权限容易成为提权漏洞一条命令扫出来# 找出所有带SUID权限的文件 find / -perm -4000 -type f 2/dev/null # 找出某个用户拥有的所有文件 find /home -user zhangsan # 找出没有属主的文件通常是异常状态 find / -nouser 2/dev/null我想特别提醒一下这些命令末尾的2/dev/null是干嘛用的。普通用户在搜索系统目录时会碰到大量权限不足的目录find会输出一堆红色的Permission denied非常干扰视线。把标准错误重定向到空设备只保留真正有用的输出这是我在实战里最常用的一个小技巧。2.3 find的逻辑组合与处理动作find支持条件之间的逻辑运算这是它区别于简单通配符搜索的地方-aand同时满足默认就是and可以省略-oor满足其中一个-not或!取反举个例子你想找/data目录下所有.conf结尾或.xml结尾的文件find /data \( -name *.conf -o -name *.xml \)这里的\(和\)是转义后的括号用来把两个条件括起来作为一个整体。很多人第一次看到会懵记住括号前要加反斜杠就行了。再比如找所有.log结尾但又不想包含access开头的文件find /var/log -name *.log ! -name access*条件确定之后处理动作是find的另一大杀器。前面的搜索只是让结果列出来配合-exec才能实现真正的自动化操作。-exec的基本格式是-exec 命令 {} \;其中{}代表当前找到的文件\;是命令结束的标志# 删除所有 .tmp 临时文件 find /tmp -name *.tmp -exec rm {} \; # 把所有 .txt 文件的权限改成644 find /data -name *.txt -exec chmod 644 {} \; # 列出所有大文件的详细信息 find / -size 500M -exec ls -lh {} \;如果你的命令不需要参数还有个更安全的替代品-delete# 删除所有 .bak 备份文件不用 -exec rm更简洁 find /data -name *.bak -delete关于-exec我分享一个实战经验批量操作前一定先不加-exec跑一遍确认搜索范围无误再决定动手。我在生产环境上见过有人写错路径前缀一条find . -name *.log -exec rm {} \;把目录下所有日志删光连备份都没有。血的教训谨慎永远是第一位的。3. grep命令实战解析从入门到文本过滤高手3.1 grep基础用法与高频参数grep的通用形式是grep [参数] 要匹配的内容 目标文件最简单的用法在单个文件里搜索关键字# 在 nginx 配置里搜 listen grep listen /etc/nginx/nginx.conf这种用法会输出包含关键字的整行内容并且默认不区分文件名。如果搜的是多个文件grep会聪明的在每行前面加上文件名前缀方便你定位。真正让我觉得grep强大的是它那几十个参数。我按使用频率从高到低排一个梯队第一梯队几乎是每条命令都要用的# 递归搜索整个目录 grep -r ERROR /var/log/ # 忽略大小写 grep -i error app.log # 显示匹配行所在的行号 grep -n error app.log # 只显示包含匹配的文件名不显示具体内容 grep -l error /var/log/*.log # 反向匹配显示不包含关键字的行 grep -v INFO app.log第二梯队进阶场景经常用# 显示匹配行的后2行上下文 grep -A 2 exception app.log # 显示匹配行的前2行 grep -B 2 exception app.log # 显示匹配行前后各2行 grep -C 2 exception app.log # 统计匹配的行数 grep -c ERROR app.log # 只输出匹配的部分而不是整行 grep -o [0-9]\ app.log这里我想单独说说-A、-B、-C这三个参数的价值。排查线上问题时光看到一行java.lang.NullPointerException没用你需要知道这个异常发生在什么上下文里。grep -A 10 NullPointerException能把异常后面的堆栈信息一起捞出来排查效率翻倍。3.2 正则表达式与扩展匹配grep和正则表达式是绑定的这也是它真正强大的地方。记不住所有正则是正常的但下面的几个核心元字符我认为是必须掌握的元字符含义示例.匹配任意单个字符grep a.c能匹配 abc、adc*匹配前一个字符0次或多次grep ab*c能匹配 ac、abc、abbc^匹配行首grep ^#$匹配行尾grep error$[abc]匹配方括号内任意一个字符grep [0-9]匹配数字\{n,m\}前一个字符出现n到m次grep a\{3\}匹配连续三个a默认情况下grep用的是基础正则表达式BRE有些元字符比如、|、?需要转义才能用非常反直觉。所以我建议你在写正则时直接加-E参数切换到扩展正则表达式ERE语法更自然# 扩展正则匹配 error 或 warning grep -E error|warning app.log # 匹配连续2-3个数字 grep -E [0-9]{2,3} app.log # 匹配以 # 开头或空行 grep -E ^#|^$ config.conf我记得自己第一次用grep时写grep error|warning死活不出结果后来才知道基础正则里|是特殊字符需要写成\|。自从知道-E这个参数后就再也没用过基础正则可。顺带一提-P参数还能启用Perl兼容正则PCRE支持更复杂的花活比如后向引用但日常使用-E足够应付九成的场景了。3.3 实战场景日志过滤、进程筛选与统计计数grep的典型应用场景太多我挑三个最接地气的讲。第一个场景日志分析。比如app.log里刷了大量日志你只想看报错# 排除INFO级别只看ERROR和WARN grep -E ERROR|WARN app.log | grep -v 超时重试 # 找出某个时间段内的异常 grep 2025-03-10 14:3[0-9] app.log | grep Exception第二种过滤排除是grep的高频用法配合管道|能把日志越刷越干净直到只剩下你想要的那几条。曾经有个客户反馈接口偶发超时日志里却找不到规律我用grep 接口名 access.log | grep -v 200一看发现是某一类参数触发了慢查询问题当场定位。第二个场景进程与端口排查。ps -ef | grep tomcat这句话在热搜里出现了确实是最典型的组合。但这里有一个隐藏的坑# 这样搜会同时匹配到tomcat进程和grep自己 ps -ef | grep tomcat # 更专业的写法 ps -ef | grep tomcat | grep -v grep为什么会匹配到grep自己因为ps -ef输出的进程列表里本身包含一条grep tomcat的命令行它会显示成一条进程。所以第一次用的新手往往会看到两条结果多出来的那条就是自己。更专业的做法是直接用pgrep命令# 直接看tomcat的进程PID pgrep -l tomcat第三类场景是统计计数。排查问题时“这个错误出现了多少次”是最高频问题grep -c直接给答案# 统计日志里Exception出现的次数 grep -c Exception app.log # 按时间维度统计假设日志每分钟一行 grep 2025-03-10 15: app.log | grep -c ERROR # 统计每个IP的访问次数配合sort和uniq grep -o [0-9]\\.[0-9]\\.[0-9]\\.[0-9]\ access.log | sort | uniq -c | sort -rn最后那条命令我几乎每周都用把访问日志里的IP提取出来排序去重统计次数再按数值倒序排巡检谁在刷接口、谁在爬数据一眼看穿。grep配合管道里的其他命令能力边界远超它本身。4. find与grep强强联合管道、xargs与-exec的实战组合4.1 xargs与-print0处理文件名带空格的关键技巧find和grep最常见的组合方式是把find的输出结果交给grep去过滤或用xargs接管。比如找项目下所有JavaScript文件并且只看包含TODO的文件# 先find出所有js文件再用grep过滤内容 find . -name *.js | xargs grep -l TODO这里的xargs是把前一个命令的输出作为参数传给后面的命令。它比-exec高效的地方在于xargs会分批把文件名拼成一条命令执行而不是每个文件启动一次进程。文件数量多的时候性能差距非常明显。但这里面藏着一个经典大坑——文件名里有空格。比如一个文件叫my project.js管道拼接后会变成grep TODO my project.jsgrep会把它当成两个文件名处理直接报错或漏匹配。解决方案是find的-print0配合xargs的-0参数# 用空字符分隔文件名完美兼容空格、换行等特殊字符 find . -name *.js -print0 | xargs -0 grep -l TODO这是我强烈建议养成习惯的写法。项目在Linux上还好一旦涉及从Windows上传过来的文件各种带空格内容的名字比比皆是不加-print0准会出幺蛾子。4.2 综合案例演练从找文件到定位问题一气呵成我想用一个真实的排查案例把find和grep的组合走一遍完整流程。假设现在是凌晨两点监控报警说服务器的磁盘使用率超过90%。我上服务器后的第一件事就是找大文件# 找出 / 根目录下所有超过500M的文件并列出详细信息 find / -xdev -size 500M -exec ls -lh {} \; 2/dev/null-xdev这个参数的意思是“不要跨文件系统”。它会限制find只在根目录所在的磁盘分区里搜索不会跑到/proc、/sys这些虚拟文件系统或者挂载的其他磁盘上去既提速又避免各种干扰。找到一个大日志文件/var/log/app/big.log之后我想看它到底是哪种错误最多# 提取最常见的关键字 grep -o ERROR:.* /var/log/app/big.log | awk {print $2} | sort | uniq -c | sort -rn | head -20如果发现某个接口的错误刷屏我进一步定位时间分布# 看错误集中在哪个小时 grep -o 2025-03-10 [0-9][0-9]: /var/log/app/big.log | uniq -c最后确定这个文件就是磁盘爆掉的元凶但为了解决业务问题暂时不能删。那我就把一周前的备份日志用同一套流程挪走或压缩# 找到7天前的日志并压缩归档 find /var/log/app/ -name *.log -mtime 7 -exec gzip {} \;这一个排查流程走下来find用了三次找大文件、找旧文件grep用了两次提取关键字、统计时间分布中间还穿插了sort、uniq、awk这些文本处理工具。Linux命令本身不复杂复杂的是怎么把它们串成一条自然流畅的处理链。4.3 find与-exec的几个高级模式-exec其实支持一种类似管道的方式用代替\;表示把所有文件一次性传入命令而不是逐个执行。看看这两者的区别# 逐个文件执行ls每个文件启动一次进程 find /data -name *.txt -exec ls -l {} \; # 一次性把所有文件传给ls只启动一次进程 find /data -name *.txt -exec ls -l {} 文件数量少的时候差别不大一旦遇到几百上千个文件性能差距很明显。这种写法会把所有找到的文件拼成一条长命令执行效率高得多。还有一点值得提的是-exec后面可以跟任何命令包括你自己写的脚本。我曾在备份任务里这样写过# 找到配置目录下的文件并调用备份脚本 find /etc/nginx/ -type f \( -name *.conf -o -name *.pem \) -exec /opt/scripts/backup_config.sh {} \;这种写法在你需要“找到文件后做一系列定制化处理”时非常好用。完全不用担心-exec太局限它本质上是交给shell执行的任意命令只有想不到没有做不到。5. 常见报错与排查技巧实录5.1 高频报错信息与解决方案用find和grep时踩过的坑那个个都是真金白银换来的经验。我把高频报错整理成一张速查表报错信息原因解决方案find: paths must precede expression路径写在了表达式的后面把路径放到find命令后面第一位如find / -name *.loggrep: xxx: No such file or directory目标路径不存在或文件名里有空格导致被拆分确认路径文件名带空格时用引号包住或用-print0grep: binary file matches搜索的文件是二进制文件grep默认输出提示不显示内容加-a参数把二进制文件当文本处理或加-I忽略二进制find: ‘/proc/xxx’: Permission denied普通用户无法访问某些系统目录搜索时加2/dev/null过滤噪音或必要时用sudoxargs: unmatched single quote文件名里有单引号等特殊字符使用find ... -print0 | xargs -0 ...还有一个特别隐蔽的坑。如果你用grep搜日志想要排除某个关键词但搜出来的结果还是包含它十有八九是关键词本身有特殊字符。比如我想排除含括号的行直接grep -v (没问题但排除含[或]的行就不行因为方括号在正则里是元字符。这时候要用-F参数把模式当成纯文本而不是正则grep -F include nginx.conf-F、-E、-P这三个可以说是grep的三大模式开关分别对应纯文本、扩展正则、Perl正则。搞不清用哪个时就默念普通搜索用-F写正则用-E要复杂断言才用-P。5.2 权威经验提升效率的四个小技巧最后分享几个我实际工作中沉淀下来的技巧虽然简单但能显著提升日常效率。技巧一给高频组合命令起别名。在~/.bashrc里加几行以后手都不用打全# 查找当前目录下所有包含关键字的文件 alias fgrepgrep -rn --colorauto # 找出磁盘上所有大文件 alias bigfilesfind / -xdev -size 100M 2/dev/null技巧二顺序问题find搜索条件里的-name建议放在前面-type、-size之类的放后面。find在执行时有一定的剪枝优化先按名字过滤能减少后续条件的计算量遇到海量目录时性能差异明显。技巧三grep -r和find xargs grep的性能取舍。grep -r简单直接但会扫描所有文件类型包括二进制。如果项目很大建议用find -type f先剔除不必要文件再交给grep处理# 全局搜索源码文件中的函数调用跳过node_modules和二进制 find /opt/myapp -type f \( -name *.java -o -name *.xml \) ! -path */target/* -print0 | xargs -0 grep -l getUserInfo技巧四用好grep -v来聚焦关键信息。日志里大量警告信息会让你失去焦点学会用反向排除逼出真正的错误。但要注意排除条件别太宽泛我见过有人grep -v WARN后把唯一一条隐藏的WARN也排掉了反而漏掉了有效线索。5.3 小白最容易踩的五个坑这个板块写给刚开始学Linux的读者。以下五个坑我几乎每周都能在论坛或带新人时见到第一find的路径忘记写导致结果为空。find -name *.log在不指定路径时默认搜索当前目录但有些人才接触时会以为这样是全盘搜索。不是。全盘搜索要写find / -name *.log。第二grep搜不到中文字符。这是编码问题日志文件是GBK编码grep默认按UTF-8处理。解决办法是先iconv -f gbk -t utf8 日志.log | grep 关键字转码后再搜。第三find执行卡死。很多人执行find / -name *.conf发现屏幕刷个不停那是因为把/proc、/sys这些虚拟文件系统也搜进去了。光给虚拟文件系统加2/dev/null没用必须加-xdev跳过其他挂载点或者用-prune排除目录# 排除proc和sys目录 find / -path /proc -prune -o -path /sys -prune -o -name *.conf -print第四grep搜了压缩文件。如果日志已经用gzip归档直接grep -r搜不到内容。要搜压缩包里的文本用zgrep命令用法和grep一模一样但能直接解压搜索zgrep Exception /var/log/app/*.gz第五管道符号用反引号代替。ps -ef | grep java有时候会写成ps -ef \grep java这种老式写法在Shell脚本里容易出歧义建议一律使用管道符|。虽然功能相近但管道符写起来更清晰也更符合现代Shell的习惯。6. 两条命令组合的进阶场景构建自己的查找工作流6.1 基于find的自动化脚本清理过期文件我日常管理的服务器上都会挂一个用find写的小脚本定时清理过期临时文件。这可能是find在生产环境里最常见的自动化用途了#!/bin/bash # 清理超过7天的临时文件 find /tmp -type f -mtime 7 -exec rm -f {} \; # 清理超过30天的备份文件 find /backup -name *.tar.gz -mtime 30 -delete # 清空超过100MB的core dump文件 find /cores -type f -size 100M -exec rm -f {} \;这个脚本放在crontab里每天凌晨执行一次我基本就再也没被磁盘告警打扰过。写这类脚本时有三个注意点第一路径一定写绝对路径防止crontab环境变量不同导致找不到目录第二-mtime 7和-mtime -7别搞混7才是7天以前-7是7天以内第三先手动执行一遍验证再挂定时任务别上来就写进crontab。6.2 基于grep的日志排查工作流从粗筛到精确定位排查线上问题时我习惯用grep构建一套从粗到细的筛选流程。比如用户反馈“页面打不开”我会上服务器走这套流程# 第一步粗筛看5分钟内有没有5xx错误 grep -E 2025:0[3-4]: access.log | grep 50[0-9] | head -20 # 第二步按用户ID或接口过滤 grep userId123456 access.log | grep error # 第三步看具体的堆栈或错误详情 grep -A 20 2025:03:10 14:3[0-9].*Exception app.log | tail -40这套流程的关键在于先用粗粒度条件缩小范围再用精细条件定位单条记录最后用上下文参数查看完整信息。如果你一上来就用最精确的关键字去grep往往因为对日志格式不够了解而什么都搜不到。先看看日志长什么样再逐步加过滤条件是日志排查的黄金法则。6.3 跨场景实战把find和grep用在项目开发中不只是运维开发者在日常工作中也离不开这两个命令。举个我自己的例子。接手一个老项目时要做代码审计我想知道哪些地方调了废弃接口# 在整个项目源码中搜索强制下线的接口 find . -name *.java ! -path */target/* -print0 | xargs -0 grep -n deprecatedApi # 统计这个接口被调用的次数 find . -name *.java ! -path */target/* -print0 | xargs -0 grep -c deprecatedApi | grep -v :0第二条命令的输出会是每个文件一行形如./src/Main.java:3一眼就能看出哪个文件里调用次数最多优先改哪里。这种“先范围后内容”的组合比单纯grep -r deprecatedApi要精准得多因为find条件帮你排除了编译产物、测试代码等无关文件。6.4 查找网络热点问题的思路一个实际案例其实很多人带着热搜里的问题来找答案比如“could not find the webview2 runtime”或者“unable to find image hello-world:latest locally”这类Docker和前端环境里的报错。遇到这种报错时思路通常是这样的这个报错是某个软件抛出来的软件安装在哪、配置文件在哪都可以用find来定位报错信息里提到的具体关键字比如webview2 runtime可以用grep去翻它的日志和配置看它在找什么路径、什么环境变量。这种“先find后grep”的排查思路比单纯搜索报错文本复制粘贴要深入得多。再比如你新增了一个虚拟机想确认系统里有没有某个命令、某个库文件# 查找命令路径 find / -name docker -type f 2/dev/null # 确认动态库是否安装了 find /usr/lib -name libssl.so* 2/dev/null # 查某个进程的具体启动参数 ps -ef | grep nginx | grep -v grep这些都是把find和grep当成日常探针的用法。说到底这两个命令是你在Linux世界里的眼睛和手一个帮你定位一个帮你洞察。写在最后这些命令怎么用才算真的会用用find和grep这么多年我最大的体会是命令本身不难难的是形成条件反射。看到一个需求脑子里立刻浮现出该用哪个命令、哪些参数、怎么组合。这种能力没有捷径只能在日常工作中反复用、刻意练。我会建议你从今天开始留心自己的操作凡是遇到“找文件”和“搜内容”的动作强制自己用find和grep去完成而不是靠图形界面的文件管理器。多数人学这两条命令时喜欢背参数表——-name、-type、-exec、-r、-n、-E……我的建议正好相反你只需要记住几个核心参数其余的等遇到特定需求再去man find、man grep查。手动查一遍记住的永远比背十遍记到的更牢固。学会用man看帮助、用--help快速浏览参数才是真正能让你独立走下去的技能。最后再分享一个小技巧。遇到复杂的查找任务先用小范围的路径比如当前目录的子目录测试命令确认结果符合预期之后再扩大搜索范围。结合我前面提到的方法加2/dev/null屏蔽权限噪音用-xdev跳过虚拟文件系统先-exec ls -lh查看而不直接-exec rm操作。这些看起来毫不起眼的习惯会在某个深夜的生产环境事故中救你一命。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →