简介:这是一份面向Linux运维与开发人员的日志查看速查手册,系统整理了tail/head、cat/tac、less/more、grep/sed、wc等常用命令的典型用法与组合技巧。资源以PDF格式呈现,共1个文件,压缩包体积仅57KB,轻量便携,适合随时查阅。目前已吸引3138人学习下载,是日常排查服务器日志、快速定位异常的高频工具总结。内容从实时监控日志增长的tail -f,到分页浏览的less与more,再到基于正则匹配的grep搜索和流编辑工具sed,每类命令都配有参数说明和实际使用场景。尤其针对生产环境常见需求,给出了按时间段过滤日志、查看关键字上下文、统计关键字出现行数、实时跟踪错误日志等组合示例,能帮助读者从只会简单查看,进阶到高效分析日志、精准定位故障。无论是初次接触Linux日志的新手,还是需要提升排障效率的资深工程师,都能从中获得即查即用的命令参考。
1. 从“看日志”到“捞日志”:先选对命令组
排查线上问题时最耗时间的往往不是定位代码逻辑,而是从几十 GB 的日志里捞出真正有用的那几十行。很多人在这一步习惯性用cat + grep,文件一大就直接把终端卡死;还有人用tail -f盯了半天,结果程序崩溃的信息早被滚屏冲掉了。Linux 日志查看命令并不复杂,真正拉开效率差距的是你得知道每个工具擅长解决什么:tail/head管首尾与实时追踪,less管大文件浏览与定位,grep/sed管过滤与切片,wc管统计。本文把这四组命令的参数细节和组合用法拆开讲,重点落在“查某个时间段的日志、取最后一次命中的上下文、实时过滤关键字”这三个高频场景上,读完可以直接照着操作。
2. 从头尾读起:tail/head 与 cat/tac 的分工和边界
2.1 tail 不只是看末尾:-f 与 -F 的取舍
tail最常用的场景是看日志文件的尾部内容,但很多人混淆了-f和-F这两个参数。tail -f filename是实时追踪文件新增内容,常用于监控正在写入的日志,比如应用在持续输出 access log 时。它有个坑:如果日志文件被轮转过(logrotate 把文件重命名、新文件顶上来),tail -f会继续盯着旧文件的句柄,监控就断了。tail -F则是按文件名重新打开,文件被轮转后会自动跟到新文件上。生产环境里我建议统一用tail -F,尤其是在日志会按天或按大小切割的场景,否则会出现“日志还在写,但终端已经不动了”的假象。
# 实时监控日志,文件轮转后仍然保持跟踪 tail -F /var/log/app/application.log # 实时监控并仅显示最后 30 行,适合刚接上手想先看最近状态 tail -30F /var/log/app/application.logtail -F后面的数字表示初始显示多少行,我习惯把它放在-F前面,语义上是“先回看 30 行,然后持续跟随”。注意区分tail -n 30和tail -30是等价的,但tail -n +30是完全不同的含义,它表示从第 30 行开始显示直到文件末尾,这里+号代表“行号起点”,不是“偏移量”。很多人刚接触时容易把这个和“往后多少行”搞混,写脚本时尤其要留心:tail -n +100输出的是第 100 行以及后面的内容,不是“最后 100 行之后的内容”这种模糊描述。
2.2 head/tail 行号参数:理解“相对位置”语义
head和tail的行号参数都支持正负号,但语义相反,放在一起对比才不容易犯晕。
# 查看前 100 行 head -n 100 app.log # 查看除最后 100 行之外的所有内容(即前 N-100 行,N 为文件总行数) head -n -100 app.log # 查看第 100 行之后的所有内容 tail -n +100 app.log # 查看最后 100 行 tail -n 100 app.log逻辑说明:head -n -100的-100表示“排除尾部 100 行”,这对日志里尾部正好是异常堆栈、想忽略掉时很有用;tail -n +100的+100表示“从第 100 行开始输出”。注意head -n +100和head -n 100的效果一样,都表示显示前 100 行,+号在 head 中不做起点解释,这里两个命令的参数语义不对称,容易记混,建议以“tail 的 + 号等价于从第 N 行起读”为唯一记忆锚点。
2.3 cat/tac 的顺序视角与 -n 编号
cat的定位是“一次性输出整个文件”,用于小文件直接看全貌,或者和管道配合做行号编号。cat -n filename会给每一行加行号输出,这个和grep -n的行号语义一致,都是“第几行”,方便后续精确引用。tac是cat的反序版本,按行倒序输出,即文件的最后一行变成输出的第一行。它的用途不是单纯“倒着看”,而是配合head实现“看末尾 N 行且带上下文”的需求。例如排错时想快速看一个几 GB 日志文件最后 100 行内容,可以直接tac huge.log | head -n 100,这不依赖文件大小预读,输出速度快,比tail更灵活的地方是你可以再管道接grep或sed做二次处理,而tail -n的输出同样能接管道,但语义上tac更适合“从后往前扫一遍”的排查场景。
# 带行号查看文件全部内容 cat -n app.log | tail -n +100 | head -n 20 # 从后往前输出最后 50 行并过滤 WARN 关键字 tac app.log | head -n 50 | grep WARNcat -n之后接tail再接head的做法本质上是先给所有行编号,再提取第 100 到第 120 行。开销在于cat -n需要全文件扫描,大文件不推荐,此场景更合适的工具是sed,它在第 4 章展开。tac对超大文件能快速出尾部内容,因为大多数文件系统对按序读尾部数据友好,且不需要把整文件加载到内存,这点在内存受限的跳板机上很有优势。
3. 翻页与定位:less 才是日志浏览的正确姿势
3.1 less 与 more:交互能力决定选择
more是最早的分页工具,只能向下翻,且翻过的地方不能再回看,在日志排查中作用非常有限。less设计上就定位为“反向兼容 more 且更强大”,支持上下方向键、翻页、搜索、跳转到指定行、前后移动等交互操作。更关键的是less默认不会把整个文件读进内存,它是一页一页按需读取的,打开一个 10 GB 的日志文件也能秒开。这个特性在查看超大日志时是刚需,很多人卡在vim打开大文件半天没响应,换成less就好了。
# 打开日志文件并显示行号 less -N app.log-N参数会在左侧显示行号,但注意这个行号是less自己计算的逻辑行号,不是文件里的物理行号,和cat -n的结果在常规文件上是一致的。按住g跳到文件首行,按G跳到文件尾行,这两个键位常用且免记。
3.2 启动定位参数:让文件一打开就停在目标位置
# 打开文件并直接定位到第 100 行 less +100g app.log # 打开文件并直接定位到最后一行 less +G app.log # 打开文件并从上往下第 100200000 字节处开始显示 less +100200000b app.log参数说明:+后面跟的是less的内部命令,100g是“跳转到第 100 行”,G是“跳转到文件末尾”,b后缀代表字节偏移。字节定位适合日志文件中一行数据特别长(比如打印了完整 JSON 报文)的场景,行号定位不够精确,字节定位能跳过前段噪声直接看中后段。实际使用中less +100g的启动速度比启动后再按100g更快,因为它在初始化时就设置好位置,不会先渲染第一屏再跳转。
3.3 在 less 内部搜索与跳转
less的搜索语法和vim类似,打开文件后直接输入/加关键字回车就能从上往下搜索,按n跳到下一个匹配项,按N(Shift+n)跳到上一个匹配项。搜索支持正则表达式,例如/ERROR|Exception可以同时匹配两种错误关键字。这里有个容易被忽略的技巧:less +/keyword app.log在启动时就直接定位到第一个匹配keyword的行,适合快速查看某类错误第一次出现的日志段落。
# 打开日志并定位到第一个 ERROR 出现的位置 less +/ERROR app.log # 打开日志,搜索 WARN 关键字并按字节位定位到 50% 位置 #(先输入 /WARN 再输入 50p 进行跳转) less +50p app.log说明:+50p中的p是百分比定位,不是字节定位,表示打开文件时把视图定位到整个文件的 50% 处。50%处的对应行在日志文件中大致等于时间轴的中间位置,如果日志按时间顺序写入,这能快速找到一个排查时间段的起点。less +/ERROR的优点是启动即命中,缺点是只定位到第一个匹配项,如果想要逐个浏览所有 ERROR 出现的上下文,用上面的/ERROR加n键翻查更合适。
4. 过滤与流式处理:grep/sed 把大日志变窄
4.1 grep 的检索参数细节:-w、-A/-B/-C 与 -o
grep的核心能力是“按模式筛选行”,但直接grep keyword file.log只给命中行,不展示上下文,排查异常堆栈时远远不够。实际工作中我常配合-C(上下文行数)一起用。-A是 after,显示匹配行之后的 N 行;-B是 before,显示之前的 N 行;-C则同时显示前后 N 行。-w强制全词匹配,避免grep Error把ErrorCode、Errors也搜进去,这在代码里关键字非常常见。
# 精确匹配 error 单词,并显示前后 5 行,输出带行号、颜色标记 grep -w "error" app.log -C 5 -n --color=always | less -R # 只输出匹配到的内容而不是整行,适合提取 IP、订单号 grep -o -E "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" app.log | sort | uniq -c--color=always在管道传给less时会让 ANSI 颜色转义码传递过去,less -R能正确解析并显示颜色,否则会显示一堆^[[01;31m之类的乱码。-o加-E的组合是真正的“内容抽取”,例如从日志里提取所有 IP 地址再统计出现次数,这是排查来源 IP 是否集中时最快的路径。注意-c统计的是行数不是匹配次数,一行里出现两次关键字也只计 1 行;grep -o配合wc -l才能统计实际出现次数,这个边界容易踩。
# 统计包含 error 的行数 grep -c "error" app.log # 统计 error 实际出现的总次数 grep -o "error" app.log | wc -l4.2 sed 按行号与时间窗口切片
sed在日志场景里最常见的两个用途是按行号切片和按时间范围切片。按行号切片适合在已知错误大约在文件中部、但不想grep大量噪声时使用。sed -n '1,100p'表示只打印第 1 到第 100 行,这个比head | tail组合少一次管道,开销更低,且不需要前置cat编号。
# 打印第 100 到 120 行 sed -n '100,120p' app.log # 删除前 100 行并输出剩余内容(相当于跳过日志头部噪声) sed '1,100d' app.log | head -n 50 # 全文替换 ERROR 为 WARN 并输出,-i 落盘时慎用 sed 's/ERROR/WARN/g' app.log > filtered.logd是删除模式,1,100d表示把第 1 到第 100 行删除后输出剩余内容,不修改原文件。s/old/new/g的g表示替换每一行里的所有匹配项,如果没有g只替换每行的第一处匹配。落盘用> filtered.log重定向而不是-i直接改原文件,因为日志文件通常是只读权限且改坏了不好恢复。时间范围切片用正则地址区间,这是sed最硬核的用法:
# 打印 2023-11-09 18:00:00 到 18:01:00 之间的日志 sed -n '/2023-11-09 18:00/,/2023-11-09 18:01/p' app.log范围和范围的边界行都会被打印,即“含头含尾”。前提是日志时间格式保持一致,如果某行没有时间戳(比如异常堆栈的后续行)不会影响区间匹配,因为sed是逐行扫描匹配起止模式。这个用法比grep '2023-11-09 18:0'更精确,后者会把 18:00 到 18:09 全搜出来。
4.3 常见误用:grep 管道前的 cat 是不是多余的?
cat app.log | grep error和grep error app.log结果完全一样,但前者多了一次进程和一次管道读写。对几十 MB 小文件无所谓,对几个 GB 的日志就是额外等几秒。我看到不少脚本里cat | grep的写法是从cat | grep | wc -l这种组合里带出来的习惯,其实可以精简掉cat。真正需要cat的场景是多个文件合并再过滤:cat a.log b.log | grep error,或者需要编号后过滤:cat -n app.log | grep error,这时cat就不是多余的。
5. 实战组合:时间窗口、末次命中与实时统计
5.1 按时间范围截取日志段
最通用的做法是用sed的正则区间匹配,但当天日志文件特别大时,可以先用grep -n找到起止时间点的行号,再交给sed按行号切片,执行效率更高。假设要提取某天的 10:00:00 到 10:00:59 的日志:
# 第一步:找到起止时间点的行号 START_LINE=$(grep -n "2024-03-18 10:00:00" app.log | head -1 | cut -d: -f1) END_LINE=$(grep -n "2024-03-18 10:01:00" app.log | head -1 | cut -d: -f1) # 第二步:按行号切片并过滤异常关键字 sed -n "${START_LINE},${END_LINE}p" app.log | grep -E "ERROR|Exception" -C 3 --color=never这里用head -1取第一个匹配到的行号,时间在日志里按顺序递增的前提下第一个匹配就是期望边界。cut -d: -f1切出冒号前的行号部分。按行号切片避免了对整个文件做正则匹配的开销,适合日志量在百万行以上时使用。
5.2 取最后一次命中的上下文
有时错误已经刷屏几百次,看最后一次发生的堆栈才有价值。用grep -A取命中后的上下文,再配合tail -n就能拿到最后一次:
# 显示最后一次 ERROR 出现的位置及它后面的 20 行 grep -n "ERROR" app.log -A 20 | tail -n 21 # 查看最后一次 ERROR 之前 5 行和之后 15 行 grep -n "ERROR" app.log -C 5 | tail -n 11grep -A 20会产生多段输出,每段包含命中行和其后的 20 行。tail -n 21取最后 21 行,正好对应最后一段命中行加 20 行上下文。-C 5 | tail -n 11则是最后一处命中前后各 5 行,加上命中行本身共 11 行,适合看崩溃点前后的状态。注意-A和-C同时使用后面的参数会覆盖,两者不要混写。
5.3 大日志实时追踪:tail -F | grep --line-buffered
实时追踪日志时直接tail -F app.log会看到全部输出,噪声太大。加一层grep做关键字过滤,但要加--line-buffered参数:
tail -F app.log | grep --line-buffered -E "ERROR|OutOfMemory|Connection refused"没有--line-buffered时,grep出于性能考虑会做块缓冲,即等到输出积累到一定量才刷出来,导致明明日志在刷但你这边好几秒没动静。--line-buffered强制每匹配到一行就立即输出,适合交互式实时监视。此场景下不要加-C上下文参数,因为tail -F是持续流,上下文行会跟着新日志不断刷新,终端滚动速度反而加快。
5.4 统计落点:wc、grep -c 与场景选择
统计日志量时wc -l最常用,但它只统计“多少行”,不是“多少字节”。查磁盘占用用ls -lh或du -h,查行数用wc -l。grep -c统计匹配行数,而grep -o | wc -l统计匹配次数,二者在“一行里多次出现同一关键字”的场景下结果不一致。例如一行日志里打印了两次timeout,grep -c timeout计 1,grep -o timeout | wc -l计 2。判断故障影响面时用前者(涉及行数),评估错误频率时用后者(实际次数),这个区别在写自动化脚本时尤其重要。补充一点:wc -l在处理不带末尾换行的文件时行数会比grep -c ""少 1,因为grep是按“换行符分隔的记录”计数,而wc -l是统计换行符数量,大日志尾部没有空行时这两个命令的结果差异容易被误判成丢日志。
本文还有配套的精品资源,点击获取