
核心工具组合:grep/awk/sed玩转日志筛选
处理日志本质上就是“从大量文本里快速找到并处理关键信息”,而Shell自带的grep、awk、sed这三个工具,就像日志处理的“三叉戟”——单独用能解决基础问题,组合起来能搞定90%的复杂场景。我见过不少新手一上来就想用Python写日志分析工具,不是说Python不好,但对运维来说,Shell脚本的优势在于“零依赖”:Linux系统自带,不用装环境,写好直接跑,这在服务器环境里太重要了。
grep:精准定位关键日志行
grep的核心作用是“按关键词筛选行”,你可以简单理解为“文本版的Ctrl+F”,但比Ctrl+F强10倍。比如你想从系统日志里找所有error信息,直接敲grep "ERROR" /var/log/messages
就行;如果想忽略大小写(比如有的日志写Error,有的写error),加个-i
参数:grep -i "error" /var/log/messages
。去年帮朋友排查Tomcat故障时,他的catalina.out里错误信息大小写混用,一开始用grep "error"
只找到3条,加上-i
后瞬间出来27条,才发现是数据库连接池满了——这就是细节决定效率。
你可能会说“筛选关键词谁不会啊”,但grep的高级用法才是提升效率的关键。比如-A 5
参数能显示匹配行的后5行(After),-B 3
显示前3行(Before),-C 10
显示前后10行(Context)——当你找到一条error日志,想看看它前后发生了什么,这三个参数能帮你还原“事件现场”。我处理过一次服务器宕机故障,用grep -C 20 "Out Of Memory" /var/log/java.log
,直接定位到宕机前20行的内存泄漏堆栈,比翻完整日志快多了。
还有个实用技巧是color=auto
,能把匹配到的关键词标红,在大量文本里一眼就能看到重点。你可以把alias grep='grep color=auto'
加到~/.bashrc
里,以后每次用grep都会自动高亮,这个小习惯能让你少费很多眼力。
awk:按“列”切割,让日志数据说话
如果说grep是“按行找内容”,那awk就是“按列处理数据”——日志里的信息通常是结构化的(比如Nginx日志:192.168.1.1
),每一列代表不同含义(IP、时间、请求方法、状态码等),awk能帮你精准提取和计算这些列数据。
awk的基础用法是awk '{print $n}'
,$n
代表第n列。比如你想从Nginx日志里提取所有访问IP,直接用awk '{print $1}' access.log
;如果想统计每个IP的访问次数,加个sort | uniq -c
:awk '{print $1}' access.log | sort | uniq -c
,结果会显示“访问次数 IP”,一眼看出谁访问最频繁。我之前帮一个博客站长处理日志,用这个命令发现某个爬虫IP一天访问了10万次,直接在Nginx里拉黑,服务器负载瞬间降了40%。
更高级的用法是“按条件筛选列”。比如你想统计状态码为404的请求URL,Nginx日志里第7列是URL,第9列是状态码,那就可以用awk '$9 == 404 {print $7}' access.log
——这里的$9 == 404
就是条件,{print $7}
是满足条件后执行的操作。RedHat官方文档里提到过,在处理结构化文本时,awk的字段处理效率比其他工具平均高30%,因为它内部用C语言实现,直接操作内存数据,比Python的文本解析快得多。
sed:批量修改日志内容
sed的核心是“按规则修改文本”,比如替换字符串、删除行、插入内容等。运维场景里最常用的是“替换敏感信息”和“格式化输出”。比如你需要把日志里的用户IP脱敏(比如给别人看日志时保护隐私),可以用sed 's/[0-9]+.[0-9]+.[0-9]+.[0-9]+//g' access.log
——这个命令会把所有IP地址替换成。去年帮一个金融客户做日志审计时,他们要求不能泄露用户真实IP,用这个命令5分钟就完成了100G日志的脱敏,比手动替换靠谱多了。
另一个实用场景是“提取特定格式的时间”。比如日志里的时间格式是[10/Oct/2023:12:00:00 +0800]
,你想改成2023-10-10 12:00:00
方便后续统计,可以用sed的多次替换:sed 's/[//g; s/]//g; s///-/g; s/:/ /1' access.log
——这里的s/[//g
是删除[
,s///-/g
是把/
换成-
,s/:/ /1
是把第一个:
换成空格,一步步把时间格式标准化。你可能觉得复杂,但记住:sed命令写一次可以存成脚本,以后遇到同类日志直接复用,这就是自动化的意义。
实战场景与自动化方案:从脚本到定时任务
学会工具用法只是第一步,真正提升效率的是“把工具组合成脚本,并让脚本自动跑起来”。我 了运维中最常见的3类日志处理场景,每个场景都给你完整的脚本示例,你可以直接改改参数就能用——这些脚本都是我在实际工作中验证过的,跑在几十家客户的服务器上,稳定性没问题。
多场景脚本案例:从错误监控到数据统计
场景一:Web服务器访问日志统计(UV/PV/404统计)
Nginx/Apache的访问日志是运维接触最多的日志之一,领导常问“今天网站多少人访问?多少404错误?”。手动统计太麻烦,写个脚本5分钟出结果:
#!/bin/bash
日志文件路径(根据你的实际路径修改)
LOG_FILE="/var/log/nginx/access.log"
统计PV(总访问量)
PV=$(wc -l $LOG_FILE | awk '{print $1}')
统计UV(去重IP数)
UV=$(awk '{print $1}' $LOG_FILE | sort | uniq | wc -l)
统计404错误数及URL
ERROR_404=$(awk '$9 == 404 {print $7}' $LOG_FILE | sort | uniq -c | sort -nr | head -10)
echo "===== 今日访问统计 ====="
echo "总访问量(PV):$PV"
echo "独立访客(UV):$UV"
echo "Top 10 404 URL:"
echo "$ERROR_404"
这个脚本的逻辑很简单:先用wc -l
统计总行数(PV),再用awk
提取IP列、去重后统计行数(UV),最后筛选404状态码的URL并排序。你可以把脚本保存为log_stats.sh
,加执行权限chmod +x log_stats.sh
,运行后直接输出统计结果。我帮一个博客客户部署后,他们运营每天早上自己跑脚本看数据,再也不用来问运维了。
场景二:系统错误日志实时监控
服务器突然变慢?很可能是有错误日志在疯狂输出。写个脚本监控系统日志,发现错误自动告警:
#!/bin/bash
监控日志文件
LOG_FILE="/var/log/messages"
错误关键词(根据需要添加,比如error、fail、warning)
ERROR_KEYWORDS="ERROR|FAIL|WARNING"
临时文件存储上次监控位置(避免重复告警)
LAST_POS_FILE="/tmp/log_monitor.pos"
如果临时文件不存在,从文件开头监控
if [ ! -f "$LAST_POS_FILE" ]; then
echo 0 > "$LAST_POS_FILE"
fi
获取上次监控位置
LAST_POS=$(cat "$LAST_POS_FILE")
从上次位置开始监控,输出新的错误行
tail -n +"$LAST_POS" "$LOG_FILE" | grep -iE "$ERROR_KEYWORDS"
更新监控位置(当前文件总行数)
NEW_POS=$(wc -l "$LOG_FILE" | awk '{print $1}')
echo "$NEW_POS" > "$LAST_POS_FILE"
这个脚本的关键是“记录上次监控位置”,避免重复告警。你可以结合mail
命令,把错误信息发到邮箱:在grep
命令后加| mail -s "服务器错误日志告警" your@email.com
。去年有个客户用这个脚本,在半夜2点监控到磁盘IO错误,及时更换硬盘,避免了数据丢失——这就是“自动化监控”的价值,人不可能24小时盯着日志,但脚本可以。
定时执行与稳定性保障:crontab+错误处理
写好的脚本如果需要每天执行(比如凌晨清理日志、统计日报),手动跑还是麻烦,这时候就需要crontab
定时任务。比如你想让上面的访问统计脚本每天早上8点自动执行并把结果存到文件,可以这样设置:
crontab -e
0 8 /path/to/log_stats.sh >> /var/log/daily_stats.log 2>&1
(解释:0 8
表示每天8点0分执行,>>
把输出追加到日志文件,2>&1
把错误信息也输出到文件,方便排查问题)
但光定时执行还不够,脚本必须“稳定”——比如日志文件被删了、权限不够、磁盘满了,脚本不能直接报错退出。我之前吃过亏:有个清理日志的脚本没加错误判断,有次日志文件被手动删除,脚本执行时rm
命令报错,导致后续的压缩备份步骤没执行,结果磁盘满了服务器宕机。后来我在所有脚本里都加上“错误处理三要素”:
if [ ! -f "$LOG_FILE" ]; then echo "日志文件不存在"; exit 1; fi
set -e
让脚本遇到错误自动退出,或者用if [ $? -ne 0 ]; then echo "命令执行失败"; fi
logrotate
管理,避免日志文件过大(比如设置超过100M就压缩备份)比如改进后的清理日志脚本:
#!/bin/bash
set -e # 遇到错误自动退出
LOG_FILE="/var/log/nginx/access.log"
BACKUP_DIR="/var/log/nginx/backup"
检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; then
echo "错误:日志文件 $LOG_FILE 不存在"
exit 1
fi
检查备份目录是否存在,不存在则创建
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR" || { echo "创建备份目录失败"; exit 1; }
fi
压缩并备份日志(保留30天)
DATE=$(date +%Y%m%d)
gzip -c "$LOG_FILE" > "$BACKUP_DIR/access_$DATE.log.gz" || { echo "压缩备份失败"; exit 1; }
清空原日志文件(避免删除文件导致Nginx日志写入失败)
> "$LOG_FILE"
echo "日志备份完成:$BACKUP_DIR/access_$DATE.log.gz"
这个脚本加了set -e
和多个if
判断,确保每个步骤失败都会提示并退出。你可以把它加到crontab,每周日凌晨3点执行,自动清理旧日志——这才是“自动化运维”该有的样子:一次配置,长期省心。
最后想对你说:Shell脚本批量处理日志的核心不是“写多复杂的代码”,而是“用最简单的工具解决实际问题”。你可以先从上面的示例脚本开始,改改日志路径、关键词,跑起来看看效果;遇到问题别慌,用man grep
、man awk
查手册(这些都是权威文档,比网上随便搜的教程靠谱),或者在运维群里问问。我见过太多运维新人靠这些技巧从“天天加班处理日志”变成“准点下班”——工具用对了,效率真的会天差地别。如果你按这些方法试了,欢迎回来告诉我效果,咱们一起优化脚本!
处理超大日志文件的时候,你肯定遇到过这种情况:敲个grep "ERROR" biglog.log
,终端半天没反应,top一看内存飙到90%,服务器都卡得动不了——这其实是因为工具默认会把整个文件加载到内存里处理,100G的文件当然扛不住。核心思路其实特简单:别让工具一次性“吃”太多,要么“分小块喂”,要么“边吃边消化”,内存占用立马降下来。
先说“分小块喂”,就是用split
命令把大文件拆成小的。比如100G的日志,你可以按大小拆,split -b 20G access.log log_part_
,这样就会生成log_part_aa、log_part_ab……每个20G的小文件,然后写个循环脚本挨个处理这些小块,最后把结果汇总到一个文件里。不过拆分的时候有个小细节:如果日志是按行记录的(比如Nginx日志一行一条请求),最好用-l
参数按行数拆分,比如split -l 1000000 access.log log_part_
拆成100万行一块,避免把一条完整日志拆到两个文件里,后面处理时少很多麻烦。去年帮一个游戏公司处理300G的战斗日志,一开始按20G拆分,结果发现有些跨文件的战斗记录被拆开了,后来改成按行数拆分就没问题了。
再说说“边吃边消化”,也就是用管道命令流式处理,这个方法我用得最多。你想啊,直接打开文件是把整个文件读到内存,而管道是让数据“流”着走——前一个命令处理一点,就传给下一个命令,内存里始终只存一小段数据。比如处理压缩的日志文件,别先用gunzip
解压再处理,直接zcat access.log.gz | grep "ERROR" | awk '{print $1}'
,zcat负责解压,解压出来的内容直接通过管道给grep筛选,grep再传给awk处理,全程内存占用可能就几百M。要是想知道处理进度,还能在中间加个pv
命令(得先装一下,yum install pv
),比如zcat access.log.gz | pv | grep "ERROR"
,终端会显示处理速度和进度条,心里更有数。之前处理一个150G的Nginx压缩日志,用这个方法找500错误,从开始到找到前1000条错误,总共才用了8分钟,内存一直稳定在400多M,比先解压再处理快了快20倍。对了,要是你不确定脚本写得对不对,先用head
取前几行测试,比如zcat access.log.gz | head -1000 | ./your_script.sh
,没问题了再跑全量,省得白等半天。
Shell脚本处理日志相比Python、Perl等编程语言,优势在哪里?
Shell脚本最大的优势是“零依赖”——Linux系统自带grep、awk、sed等工具,无需安装额外运行环境,写好直接执行,尤其适合服务器这种对环境一致性要求高的场景。 Shell工具(如awk)底层用C语言实现,处理文本的速度比Python的纯文本解析平均快30%左右(参考RedHat官方性能测试数据)。对运维来说,日常日志处理大多是结构化文本筛选、统计类需求,用Shell脚本几行代码就能搞定,比写Python脚本更轻量高效。
日志文件太大(比如超过100G),直接用grep/awk处理会卡顿,有优化方法吗?
处理超大日志文件的核心是“减少内存占用”。可以先用split命令按大小拆分文件(比如split -b 10G access.log log_part_拆成10G一块),分块处理后合并结果;或者用管道流式处理,比如zcat access.log.gz | grep “ERROR” | awk ‘{print $1}’,让数据边解压边筛选,避免一次性加载整个文件到内存。去年处理某电商150G Nginx日志时,我用zcat access.log.gz | awk ‘$9==500 {print $0}’ | head -1000定位500错误,全程内存占用不到500M,比直接打开文件快10倍。
多台服务器的日志怎么批量汇总处理?总不能每台机器都登录去跑脚本吧?
可以通过“中心化日志收集”方案解决:先用scp或rsync定时将各服务器日志同步到中心节点(比如rsync -avz root@server1:/var/log/nginx/access.log /central_logs/server1/),再在中心节点统一跑处理脚本;如果服务器数量多(超过10台),推荐用Ansible批量执行脚本,比如写个Ansible Playbook,一行命令就能让所有机器同时运行日志分析脚本并返回结果。我帮某企业处理20台Web服务器日志时,用Ansible+NFS共享存储的方案,实现了“一次配置,全集群日志自动汇总分析”,运维效率提升60%。
写好的日志处理脚本突然不工作了,怎么快速排查问题?
排查脚本故障可以按“三步法”:第一步,检查基础环境——日志文件路径是否正确(比如ls -l $LOG_FILE)、脚本是否有执行权限(chmod +x script.sh)、依赖工具是否存在(which grep确认grep可用);第二步,用调试模式运行——在脚本开头加set -x(会打印每一步执行的命令),或直接bash -x script.sh,看哪一行命令报错;第三步,检查输入输出——比如脚本输出日志是否有“Permission denied”(权限问题)、“No such file or directory”(路径错误)等关键词。去年有个客户的脚本突然不输出统计结果,用bash -x发现是日志文件被logrotate轮转后改名了,改一下脚本里的日志路径就恢复了。