
在Linux上用命令查看文件几乎是每个运维、开发、测试每天都要做的事。可说实话能把cat、less、tail、grep、sed、awk这些文件查看命令和文本处理工具用到顺手的人我面试过几百个候选人其实并不多。大多数人的状态是知道cat能看文件知道grep能过滤关键词但一遇到大日志、乱码、实时跟踪、需要从几万行里精确提取一段数据这些真实场景就开始手忙脚乱甚至直接把终端卡死。这篇不打算罗列命令手册而是从最基础的cat一路拆到awk把每条命令的适用边界、常用参数、性能差异和隐藏坑点都讲透顺便把面试里经常出现的考察点一并梳理清楚。无论你是刚接触Linux的初学者还是已经在生产环境摸爬滚打多年的老手应该都能从中找到一些值得细看的细节。1. 文件查看命令全景先想清楚你到底要干什么1.1 查看文件的三个层次浏览、检索、加工很多人觉得查看文件就是把文件内容显示到屏幕上这是一个非常危险的误解。我见过有同事用cat打开一个2GB的GC日志结果终端直接卡死最后只能切TTY去kill进程。实际上文件查看在真实工作中至少分成三个完全不同的层次对应的工具也完全不同。第一个层次是浏览目标是把文件内容或其中一部分读出来。cat、less、more、head、tail都属于这一类。它们解决的是这个文件里写了什么的问题但各自擅长的尺寸完全不同cat适合小文件less适合大文件head和tail适合只关心开头或结尾的场景。第二个层次是检索目标是从大量内容中找出符合某种模式的行。代表工具是grep。它解决的是我要的那部分内容在哪的问题是日志排查中出场率最高的命令。第三个层次是加工目标是对文本进行抽取、替换、排序、统计输出一份处理后的结果。sed、awk、cut、sort、uniq、tr这些工具负责这个层次解决的是怎么把原始内容变成我想要的数据格式的问题。用生活里的类比来说浏览是逛超市时看货架检索是打开地图找某款商品的位置加工则是把买到的东西切块、调味、做成一道菜。很多新手一上来就在做加工的场景里只会用cat这不是工具不够用是没搞清楚自己到底属于哪个层次。1.2 选型逻辑需求决定工具别为用而用在明确了三个层次之后选型就变成了一道简单的判断题。我自己带新人时会让他们记住下面这张场景-命令对照清单基本覆盖了日常95%的查看需求小文件完整浏览用cat加上-n还能显示行号但超过几百MB的文件就别碰了。大文件浏览或搜索定位用less。它按需读页打开几个GB的文件也不会把内存打爆。只关心文件开头用head -n 100只关心文件结尾用tail -n 100。实时跟踪不断增长的日志用tail -f或less F。需要从一堆日志里捞关键词用grep配合-A、-B、-C参数能拿到上下文。需要按列抽取、替换、统计就必须进入加工层用cut、sed、awk的组合。这条清单背后有一个核心原则不要让一个命令承担它不擅长的工作。比如用cat去找内容你可以把整个文件输出后再靠肉眼搜索这在文件小的时候勉强能用文件一大就是灾难。反过来用awk去做纯浏览也是杀鸡用牛刀写起来费劲执行效率还不如head。还有一个非常现实的点命令的执行性能。cat是把文件内容从磁盘读出来往终端写终端I/O是巨大的瓶颈grep只扫描匹配的行但扫描过程中也要逐行读文件awk是逐行解释执行写得多跑得慢less则有专门的优化按页面加载跳转时不重读全文件。理解了性能差异你就不会在生产环境乱来。2. 基础查看命令拆解cat、head、tail、less、more2.1 cat最常见的命令名最少被用对的功能cat大概是Linux里被误解最深的命令。它的名字不是看的意思而是concatenate本来是用于连接文件的。cat a.txt b.txt c.txt把两个文件首尾相接输出到第三个文件这才是它最原始的家常本事。至于查看文件这个用途只是因为没有重定向时它会默认输出到标准输出恰好看上去像显示内容而已。cat的常用参数里-n显示行号-b只给非空行编号-s压缩连续的空行-A把tab显示成^I、行尾显示成$适合排查乱码和隐藏字符。举个例子一个从Windows上传过来的脚本总是报错你肉眼根本看不到问题执行cat -A script.sh就能看到每一行末尾都有^M$这是典型的CRLF换行符在捣鬼。cat还有一个容易让人栽跟头的特性它把文件内容直接倾泻到终端。文件小还好文件一大终端缓冲区直接把整个屏幕刷掉前面的内容根本看不过来。更要命的是如果你在管道里用cat处理超大文件比如新手的经典操作cat huge.log | grep error这其实是多余的纯粹浪费了一次磁盘读和一次管道写。正确的姿势是直接grep error huge.log省掉cat那一段性能肉眼可见地提升。这个习惯养成后在几GB的日志文件上差异可能从几十秒缩短到几秒。2.2 head 与 tail日志查看的两大主角如果说cat是查看界的万能劣化版那head和tail就是精准切割的专用工具。head -n 10 file看前10行tail -n 10 file看最后10行这在查看大文件时比cat安全得多因为命令只扫描需要的那部分不会把整个文件读进内存。日志监控场景里tail -f file是标配中的标配它会持续跟踪文件尾部新追加的内容实时刷在屏幕上。生产环境排查时我最常用的组合是tail -f app.log | grep ERROR把实时日志流里过滤出报错信息一边盯着一边等待问题复现。tail有一个非常隐蔽的坑就是-f和-F的区别。-f按文件描述符跟踪当文件被logrotate改名后它就跟丢了-F按文件名跟踪文件被轮转后还能自动重新打开新文件。所以在有日志切割机制的生产服务器上用tail -F才是稳妥的用tail -f你会发现日志刷着刷着突然静止了怎么等都不再输出。一个小参数差异能让你的监控脚本在凌晨无声地失效。另外要查看文件中间某一段时很多人第一反应是head -n 5000 file | tail -n 100这确实能得到501到600行之间的内容但它要读完整文件前5000行。更轻量的是sed -n 501,600p file逐行扫描到600行就停不需要额外管道。下一节细说sed时会再展开。2.3 less 与 more交互式浏览less 才是完全体我现在几乎不用more但在很多老书和面试题里都还会有它。more的体验非常原始只能往下翻不能往回翻搜索功能也弱逼急了只能退出重开。less是more的增强版名字本身就是一个梗less is more——less比more强。less处理大文件的性能非常出色它打开文件后不会把全部内容加载进内存而是按需读入当前屏幕需要的那部分。你按CtrlF向下翻页按CtrlB向上翻页按/输入关键词搜索按n跳到下一个匹配按N回到上一个匹配按g跳到文件首行按G跳到文件末行按q退出。这些快捷键看起来琐碎但确实是查看几GB日志文件时最舒服的方案。less还有两个容易被忽略的宝藏参数。第一个是less F file它进入类似tail -f的实时跟踪模式日志新增内容会自动刷出来此时按CtrlC中断跟踪你就进入普通的浏览模式可以用/搜索刚才刷过的内容再按F重新接上实时跟踪。说实话在排查正在生产环境不断滚动的日志时这个跟随-暂停-搜索-继续跟随的流程比纯用tail -f顺手得多因为你可以随时回看历史上下文。第二个是less -N file在左边显示行号配合搜索定位非常有用。还有个小技巧在less里直接按v会用vi打开当前正在查看的文件方便你带着搜索结果直接进入编辑模式。2.4 周边辅助命令tac、nl、wc顺手提几个经常在脚本里配合使用的小工具。tac按行逆序输出最后一行最先显示逻辑上和cat正好相反有时需要倒着看日志时很有用比如查看日志文件最后出现的几条报错。nl专门用来输出行号比cat -n更可控支持-ba全行编号、-bt只给非空行编号等选项。wc用来统计文件的行数、单词数和字节数wc -l file统计总行数wc -c file统计字节数wc -w file统计单词数这三个是高频操作。需要注意的是wc统计的是字节数而不是字符数在处理UTF-8中文时wc -c的结果会比肉眼看到的字符数多因为一个中文汉字在UTF-8编码下占3个字节。如果确实要统计字符数可以用wc -m。这个细节面试里偶尔会问到也是一个容易踩坑的地方。3. 高级文本处理工具实战grep、sed、awk、cut 的组合3.1 grep从找得到到找得准grep的参数是所有人背得最多又最容易混淆的一块。grep pattern file是最基础形态但真实场景里你几乎总会加几个参数。-E把正则扩展语法打开等于使用egrep-i忽略大小写-v反向匹配把不包含pattern的行过滤出来-c只输出匹配行数-n同时输出行号。这些是必须肌肉记忆的参数。排查日志时最有价值的是上下文参数。grep -n -A 5 Exception app.log打印匹配行及其后5行-B 5是前5行-C 5是前后各5行。生产环境的报错比如Java异常堆栈信息往往在报错关键词之后用-A直接把堆栈跟出来而分析Nginx访问日志里的慢请求时-B能让你看到这个慢请求前后的访问者轨迹。用好-A/-B/-C基本等于把看到日志报错却不知道发生了什么的问题解决了一半。还有一个实战性能建议如果搜索的是固定字符串而不是正则表达式加上-F参数grep会走纯字面量匹配不涉及正则引擎速度快很多。我处理过一个大日志文件用普通grep ERROR扫描要跑40秒加上-F之后只需要15秒左右。虽然现在grep已经做了大量优化但这个习惯在超大文件上依然能带来显著收益。3.2 sed流式编辑看起来是查看实际是改写sed在处理从流里提取内容这个需求时特别擅长。sed -n 3,5p file只打印第3到5行sed -n /error/p file只打印包含error的行相当于grep的另一种写法。sed -n /server/,/}/p nginx.conf打印从匹配server的行开始到下一个}行结束的所有行这是提取配置块的利器。sed最核心的用途是替换sed s/old/new/g file把每行的old替换成new输出到标准输出原始文件不变化。只有当加上-i参数时才会原地修改文件。这里有一个非常实用的安全习惯sed -i.bak s/foo/bar/g file会在原地修改前自动生成一个file.bak备份文件一旦改错可以立即恢复。我在改生产服务器上的配置文件之前一定会用这个写法留好备份等确认没问题后再清理.bak文件。还有一点必须强调sed是流式处理它逐行读入、处理、输出不会把整个文件加载到内存。这意味着处理几个GB的文件内存占用几乎恒定。而有些新人用sed和awk处理大文件时处处担心内存爆炸其实这两个工具是基于流的根本不用慌真正要慌的是把整个文件读进内存的语言脚本比如不恰当地用Python的read()。从原理上理解sed你就不会在到底该用head还是sed提取某一段上犹豫——它们做的事情一样但sed更干净。3.3 awk把文本当表来处理的思维awk是这三个工具里最像编程语言的一个。它的默认世界观是每一行是一行记录按空格或制表符切分成多个字段$1、$2、$3分别代表第一列、第二列、第三列$0代表整行。awk {print $1, $3}提取第一列和第三列这就是awk和cut重叠的地方但awk能做的事远不止按列切分。awk里有个实战频率极高的内置变量NF代表当前行的字段数量NR代表当前行号FS代表输入字段分隔符。awk -F: {print $1} /etc/passwd按冒号切分提取每个用户的用户名这是教科书级的例子面试里出现过无数次。awk还能做条件过滤awk $3 100 access.log只打印第三列数值大于100的行这在分析耗时统计时很好用。awk里的$3是字符串但awk会自动做数值转换比grep正则过滤更直观。awk真正拉开差距的功能是统计awk {sum $2} END {print sum} file对第二列求和awk {count[$1]} END {for (ip in count) print ip, count[ip]} access.log按第一列分组计数这是很多统计数据报表的雏形。面试题里统计日志里每个IP出现的次数完整答案就是awk {count[$1]} END {for (k in count) print k, count[k]} access.log | sort -rn -k2 | head。用awk做一次分组和累加数据结构的思想全在里面了。3.4 cut、sort、uniq、tr管道上的拼图块这组命令单看都很简单但组合起来能解决几乎所有文本清洗问题。cut -d: -f1 file按冒号切分取第一列cut -c1-10 file截取每行前十个字符。和awk相比cut更轻量语法更简单适合纯切分场景awk可以理解为切分加计算加统计的完整方案。实际选用时只切分用cut切完还要算就用awk。sort默认按字典序排序-n按数值排序-r倒序-k2指定按第二列排序-t:指定分隔符。uniq去重但它只处理相邻的重复行所以正确的去重姿势永远是先sort再uniq。uniq -c会在每行前面加上出现次数这个输出配上sort -rn按次数倒序排就是排行榜的通用生成方法。tr做字符级别的替换和删除最常用的是tr -d \r把Windows换行符的\r直接删掉解决过无数CRLF问题。另一个是tr [:lower:] [:upper:]做大小写转换。这个工具极其适合作为管道的最后一道清洗工序。把这些命令串成一个经典例子就是分析访问日志里访问量最高的十个IPawk {print $1} access.log | sort | uniq -c | sort -rn | head -10整个过程像流水线awk负责把第一个字段抽出来sort让相同的IP站在一起uniq -c把相同IP合并并计数sort -rn按次数从大到小排序head -10取前十个。每一步都只做一件简单的事但组合起来就完成了一个完整的数据分析任务。这正是Linux命令行哲学的精髓小工具单一职责通过管道组合成强大小工具单一职责通过管道组合成强大系统。4. 场景化对比不同需求下的命令选型4.1 日志实时追踪tail -f 与 less F 的取舍在盯生产日志的时候tail -f和less F是两大主流方案但它们的使用体验差异很大。tail -f app.log直接把新内容刷出来配合grep过滤后很清爽但它有个让人难受的地方刷出来的内容没有历史缓冲你想回头看刚才刷过去的某条报错只能把眼睛黏在屏幕上或者提前把输出重定向到文件。less F app.log则是另一种节奏。进入后自动跟随新内容发现情况时按CtrlC停下来用/搜索关键词上下翻页分析上下文看完按F重新跟随。这种随时暂停、随时搜索、随时继续的体验让我在排查一次线上故障时彻底放弃了tail -f。那次问题的特征是日志每隔一两分钟报一条错误用tail -f刷过去就没了等我切到less F后随时停住搜索终于把完整的错误堆栈和前面的关联日志都对上了。我的建议是如果只是临时看一眼实时输出tail -f够了如果需要持续盯一段时间还要反复搜索历史内容less F是更好的选择。另外提醒一下tail -f跟丢了文件轮转的坑前面提过less F同样受这个影响但它可以通过在less里按:e重新打开文件来恢复比tail多一点容错。4.2 配置文件解读过滤注释、提取配置块排查服务问题时最常碰到的动作是看一眼某个配置文件里到底怎么配置的。直接用cat打开nginx.conf满屏的注释会让你很难受。我常用的一行命令是grep -Ev ^\s*#|^\s*$ /etc/nginx/nginx.conf-E启用扩展正则^\s*#匹配行首可能有空格后紧跟着井号的注释行^\s*$匹配空行-v把这两类都过滤掉剩下的全是有效配置。同样地想查看某个配置区块可以用sed提取sed -n /server {/,/}/p /etc/nginx/nginx.conf这个写法从匹配server {的行开始一直打印到下一个}行的范围正好是整个server块。如果你想看多个server块可以加-e重复这个表达式。等到需要快速确认某些参数时awk的按列能力就顶上来了比如提取所有listen后面的端口awk /listen/ {print $2} /etc/nginx/nginx.conf这套「grep过滤噪音、sed提取块、awk抽字段」的组合我在排查几百个节点的服务配置时反复用基本不看原始注释内容直接就能定位到可疑配置项。4.3 大文件的查看与抽样文件超过1GB后cat基本就别考虑了。除了把终端刷没之外它的完整输出还会给管道下游造成巨大压力。正确的大文件查看姿势是看前200行确认格式head -200 huge.log看尾部最新内容tail -200 huge.log看指定的中间段sed -n 500000,500100p huge.log快速统计总行数wc -l huge.log需要随机抽样查错shuf -n 100 huge.logshuf可能不少人不熟它的-n参数能从大文件里随机抽取指定行数的样本做数据抽样时非常方便。我在分析用户行为日志时常会用它先抽一小部分样本跑统计模型而不是等到全量清洗完再跑。这里还有一个大文件相关的常见误解有人以为cat huge.log | sed -n 500,600p会把文件全部读进内存其实不会管道是流式的sed一行一行处理完就丢弃内存占用依然是常数。真正危险的是某些脚本语言比如不设代地顺序读取或者直接用grep的标准输出又接上别的命令把整个处理链路拉到慢速路径上。4.4 高频面试题5000 行日志怎么处理热搜里有个场景很典型linux cat打印5000行日志。很多刚入行的人第一反应是cat log | head -5000这确实能打印前5000行但如果问的是打印第3000到3100行情况就不同了。# 打印前5000行 head -n 5000 app.log # 打印最后5000行 tail -n 5000 app.log # 打印第3000到5000行 sed -n 3000,5000p app.log # 同样效果但稍慢 tail -n 3000 app.log | head -n 2001tail -n 3000的含义是从第3000行开始到尾部的所有内容tail -n 3000 app.log | head -2001经过二次截取也能得到第3000到5000行的内容但这条链路走两次管道效率不如sed。真正面试时我还会追问一句如果文件有2GB处理第3000行附近的内容时它们分别是否会占用大量内存答案是都只有常数级内存流式处理让它们都能扛住大文件前提是别把head和tail的作用方向用反。5. 常见问题与避坑指南5.1 几个我踩过的坑第一个坑是cat二进制文件。如果不小心cat了一个二进制文件终端会涌入大量不可见字符轻则显示乱码严重时会把终端控制序列刷掉导致shell整个乱掉甚至需要重启。我的教训是拿到一个文件不确定类型时先file filename看看再用less打开至少它不会把二进制内容原样丢给终端。第二个坑是sed的原地修改。sed -i是个双刃剑改错了没有后悔药。我吃过一次亏在批量替换配置文件时因为正则写得太激进把非目标内容也替换了等反应过来只能从版本库回滚。后来在服务器上临时操作时始终保留-i.bak的参数习惯改完先检查.bak确认没问题再清理。这个习惯救了我很多次。第三个坑是uniq去重。很多人写的去重命令是uniq file但这个命令只删除相邻的重复行。如果文件里相同的行分散在各处uniq根本不会去重。必须先sort file | uniq才能达到全局去重的效果。为了拿到出现次数最多的IP这类答案sort | uniq -c | sort -rn这个流水线更是不变的经典。第四个坑是Windows文件残留的\r字符。从Windows上传的脚本经常在执行时报$\r: command not found原因就是行尾多了一个看不见的\r。排查时用cat -A script.sh行尾会显示为^M$修复可以跑sed -i s/\r$// script.sh或tr -d \r script.sh clean.sh。这个坑在混合开发环境里几乎每周都能碰到。5.2 自动化脚本里的隐藏细节把查看命令写进脚本时有几个细节比交互式使用更需要小心。首先脚本里尽量少用cat多用输入重定向或直接给命令传文件名。比如grep pattern file和grep pattern file效果相同但省去了启动一个进程的开销。管道的核心是过滤和变换不是先cat一下。其次grep在脚本里要注意退出码。grep找不到匹配时退出码是1而很多脚本默认set -e会让脚本直接终止。如果你期望找不到也没关系就要在命令后面加上|| true或者提前用条件判断来捕获退出码。这个问题在排查自动化任务静默失败时非常隐蔽。再次for循环里遍历文件时文件名可能包含空格或特殊字符。for file in *.log遇到my log 2024.log这样的名字会被拆断正确的做法是使用find配合-exec或者把IFS设置为换行符。这个细节在写日志归档脚本时特别容易翻车。最后管道命令太多时要养成用tee观察中间结果的习惯。比如一个复杂的awk统计链路过长中间结果对不上账时我会在关键节点插一个| tee /tmp/debug.txt |把中间输出存下来核对排错完再移除。这个调试方式比起一步步重跑要高效得多。我个人在实际操作中的体会是Linux文件查看这门手艺说到底是知道每个工具擅长什么然后组合起来用的工程。不要指望背下所有参数真正值钱的经验是在生产环境里被日志刷过、被乱码坑过、被大文件卡过之后沉淀下来的那套判断这一步该用tail还是less那一步该用awk还是cut先sort还是先uniq。你踩过的坑越多下一次选型就越果断。上面这些场景和方法希望能在你还没踩坑之前先把路标立在那里遇到同类问题时直接绕开。