带过不少新人,也当过很多次面试官,Linux 命令行这一关几乎是必考的。而在命令行里,grep、sed、awk 这三兄弟又是绝对的主角。市面上讲三剑客的教程一抓一大把,但真正能对着面试题把原理、用法、坑点讲透的很少。这套 100 道题,是我结合 DeepSeek 做了一轮题目梳理,再按照实际工作场景重新组织和校验过的结果,覆盖了从基础匹配到日志统计分析再到复杂文本处理的完整链路。
这篇文章不打算搞“题目列表+简单答案”那种敷衍形式,而是把 100 道题拆解成五个大模块,每个模块挑出最有代表性的题目做深度解析,顺带把面试官真正想考察的点也给你点透。不管你是准备面试的求职者,还是带新人的老手,或者单纯想把三剑客用得再溜一点的运维和开发,这篇内容应该都能让你有收获。
1. 先看题库整体:100道题是怎么设计出来的
1.1 难度梯度与题目分布
拿到这 100 道题的第一件事,不是闷头刷,而是先看题目结构。我把整个题库按难度和主题做了个交叉分类,大致分布如下表:
| 主题方向 | 初级题(了解命令) | 中级题(理解原理) | 高级题(综合实战) | 合计 |
|---|---|---|---|---|
| 正则表达式基础 | 10 | 8 | 2 | 20 |
| grep 文件匹配与过滤 | 10 | 8 | 3 | 21 |
| sed 流式编辑与替换 | 8 | 10 | 4 | 22 |
| awk 列处理与统计 | 6 | 15 | 8 | 29 |
| 三剑客组合与shell集成 | 2 | 2 | 4 | 8 |
| 合计 | 36 | 43 | 21 | 100 |
注意看 awk 的题量,占了将近三分之一,而且高级题里有一半落在 awk 上。这不是偶然。实际工作中,grep 负责“找”,sed 负责“改”,awk 负责“算”,但真正的统计、汇总、格式化输出这些硬骨头,几乎都是 awk 在扛。面试官心里也清楚,能把 awk 用得熟练的人,对文本处理的理解基本不会差。
再说难度梯度。36 道初级题主要考察“用没用过”,比如grep -c是数行数还是数字符数,sed 's/old/new/g'的 g 是什么作用,awk -F怎么指定分隔符。43 道中级题考察“懂不懂原理”,比如 grep 的 BRE 和 ERE 区别、sed 的模式空间和保持空间、awk 的 NR 和 FNR 差异。剩下的 21 道高级题基本是综合场景,比如“统计 Nginx 日志里每个 IP 的访问次数 Top10”“把第二列和第三列互换并去掉重复行”之类。
1.2 刷题的正确姿势
很多人刷题是看一题记一题答案,今天记住了,明天换一个问法又懵了。我的建议是:先建一个正则表达式的知识框架,再分工具逐个击破。
正则表达式是三剑客的公共地基。你可以在 Python、Perl 里写正则,但 Linux 三剑客用的正则又有自己的变体:基础正则(BRE)、扩展正则(ERE)、以及 PCRE(Perl 兼容正则)。grep 默认走 BRE,-E走 ERE,-P走 PCRE;sed 默认也是 BRE,-E可以切到 ERE;awk 则直接使用 ERE。不理解这层关系,很容易出现“同样的正则,在 grep 里能匹配,到 awk 里就报错”的困惑。
刷题的时候还有一个小技巧:不要只看标准答案,把答案的命令拆开,每部分是什么含义,去掉某个参数会怎样,换成另一个工具能不能实现同样的效果。三剑客很多功能是重叠的,比如“找包含某个关键字的行”,grep、sed、awk 都能做到,但各自适合的场景不同。把一道题用三种工具各写一遍,你对它们的边界感会清晰很多。
另外,这套题的整理我给 DeepSeek 提了几个细化的要求,比如“附上命令执行前后的对比”“易错点单独标注”“不要直接给答案,先给思路”,这些细节对于建立一个面试导向的复习体系很有帮助。AI 工具可以帮你批量生成题目和参考答案,但最后一定要自己做一遍,因为面试问答和实际敲命令是两码事。
2. grep 高频题组:匹配之外还有多少门道
2.1 从最简单的匹配说起:递归、排除与上下文
很多人的 grep 水平停留在grep keyword file,这远远不够。先看题库里的一道初级题:
题 7:在 /var/log 目录下所有 .log 文件中递归查找包含ERROR的行,并显示行号。
grep -rn "ERROR" /var/log --include="*.log"-r是递归,-n是显示行号,--include限定文件类型。这三个参数是实际工作中最高频的组合,没有之一。但注意,-r和-R是有区别的:-r在遇到符号链接时不会跟随,-R会。如果你处理的日志目录里存在 symlink,用-R才能查得全,但也可能因为链接循环导致意外输出。
再看一个容易答错的变体:
题 12:查找包含success但不包含failed的行。
这个需求听起来简单,但实际处理很讲究。最简单的方式是管道:
grep "success" file | grep -v "failed"但如果你更熟悉 awk,一条命令也能搞定:
awk '/success/ && !/failed/' file两道命令效果差不多,但面试官考察点不一样。第一种考察的是-v反向匹配和管道组合思路,第二种考察的是 awk 的多条件匹配能力。都写出来,能体现你的灵活度。
-v是 grep 的排除利器,但真正的坑在于:grep -v failed是排除包含failed子串的行,而不是排除单词failed本身。如果你的日志里出现not_failed或者failed_xxx,也会被排除。这种边界问题,恰恰是面试题里最爱埋的雷。
2.2 正则进阶:BRE、ERE 与 PCRE 到底差在哪
题库里有一道题特别能检验基本功:
题 23:匹配手机号格式(1 开头,第二位 3-9,总共 11 位数字),分别用 grep 默认语法和 grep -E 写出。
默认 grep 走 BRE,花括号表示次数必须转义:
grep -n '^1[3-9][0-9]\{9\}$' phones.txt而grep -E走 ERE,花括号不需要转义:
grep -nE '^1[3-9][0-9]{9}$' phones.txt这道题的考点有两个。一是你是否清楚 BRE 和 ERE 在元字符转义上的差异:BRE 里?、+、{、}、(、)这些字符默认是字面含义,要表示特殊含义必须加反斜杠;ERE 恰好相反,这些字符默认就是特殊含义,加反斜杠反而表示字面字符。二是你是否知道^和$表示行首行尾,这是做整行匹配的前提。
再进阶一点,PCRE 支持\d、\w这种更简洁的写法,所以有些面试者会直接写:
grep -P '^1[3-9]\d{9}$' phones.txt-P参数确实好用,但它依赖 PCRE 库,而且在高版本的 grep 里依然不是默认选项。在部分精简版系统或嵌入式环境里,-P可能不可用。面试时如果写-P,最好顺带说一句“这是 PCRE 扩展语法,某些环境需要额外支持”,显得你心里有数。
2.3 实战案例:日志与配置处理中的 grep
题库里的高级 grep 题基本都挂在场景上,比如:
题 41:从 access.log 中提取所有返回状态码为 500 的行,并统计数量。
这个需求 grep 只能做一半,提取行可以,统计数量要交给wc:
grep " 500 " access.log | wc -l这里有个细节:为什么要写成" 500 "而不是"500"?因为日志里可能有5000、1500这种字段,加前后空格是为了锚定独立的状态码字段。更严谨的做法是把响应码放到行尾匹配,比如grep ' 500$',具体要看你的日志格式。面试时能主动讲出这个细节,比死记命令要加分得多。
再举一个常见组合:提取 IP 并排序去重。
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort | uniq -c | sort -rn-o只输出匹配到的部分,而不是整行;sort | uniq -c统计每个 IP 出现次数;最后的sort -rn按次数从大到小排。这条命令是面试经典题,背下来不难,但能解释清楚-o的作用才算真懂。
关于 grep,我还有一个建议:日常工作中不要只依赖 grep 的默认行为。--color=auto让匹配结果高亮,排查问题的时候非常直观;-A、-B、-C可以输出匹配行的后文、前文和上下文,配合日志中的堆栈信息特别好用。这些参数看似零碎,但面试题里经常以“怎么写更高效”的形式出现。
3. sed 高频题组:流编辑器的三板斧
3.1 替换操作:定界符、转义与分组引用
sed 最核心的能力就是替换。先看一道初级题:
题 18:把文件里所有的foo替换为bar,并直接修改原文件。
sed -i 's/foo/bar/g' file.txt三个关键点。一是-i,直接修改文件;但-i的写法在不同 sed 版本里有差异,Linux 的 GNU sed 直接加-i,macOS 的 BSD sed 需要-i ''。二是s是替换命令,g是全局替换,不加g的话,每行只替换第一个匹配。三是foo里的/如果换成路径,比如替换/usr/local为/opt,定界符就要改,写成sed -i 's#/usr/local#/opt#g' file,这样可以免去大量转义。
面试官很喜欢在分组引用上出题:
题 26:把2024/01/05格式的日期转换成2024-01-05。
sed -E 's#([0-9]{4})/([0-9]{2})/([0-9]{2})#\1-\2-\3#g' file这个解法的核心是()分组和\1引用。注意-E参数,它让 sed 进入 ERE 模式,这样{}和()都不需要转义。如果你不用-E,就得写成\([0-9]\{4\}\),可读性差很多,也容易写错。
这里有个易错点必须提醒:捕获组是从(出现的顺序编号的,\1是第一个左括号对应的内容。如果正则前面还有其他分组,编号会顺延,写错\1、\2的顺序是新手最常见的问题。我建议在本地调试的时候,先不加-i,让 sed 输出到屏幕,确认无误后再加-i写回文件。
3.2 地址匹配与范围控制
sed 的地址匹配是另一个高频考点:
题 33:只删除文件第 10 行到第 20 行的内容。
sed '10,20d' filed是删除命令。这个题简单,但很多人不理解10,20d中的逗号其实是一种“范围地址”的写法,它的规则是:从匹配到第一个地址开始,到匹配到第二个地址为止。范围不限于数字,也可以混合正则:
sed '/BEGIN/,/END/d' file这条命令会删除从匹配BEGIN的行开始,到匹配END的行结束的所有内容。这种“区间删除”在清理配置文件片段、移除日志中的异常段时非常有用。
还有一道容易答错的题:
题 35:打印文件第 1 行到第 5 行,但不修改文件。
很多人的第一反应是sed -n '1,5p' file。-n关闭默认输出,p命令显式打印。如果忘了-n,结果会非常难看:sed 默认会打印所有行,而匹配到的行会被打印两次。所以sed的-n和p基本是绑定出现的,考试时只要看到p,先检查有没有-n。
3.3 处理配置文件与日志流的经典场景
题 52:把 Nginx 配置里listen 80;全部改成listen 8080;,同时备份原文件。
sed -i.bak 's/listen 80;/listen 8080;/g' nginx.conf-i.bak的意思是先备份为nginx.conf.bak,再执行替换。生产环境改配置一定要养成备份的习惯,这个参数就是为此设计的。如果你想更稳妥,可以先执行sed 's/listen 80;/listen 8080;/g' nginx.conf查看输出,确认无误后再加-i操作。
sed 还可以直接处理管道流,这是它“流编辑器”称号的来源:
tail -f app.log | sed 's/ERROR/【错误】/'这个用法在处理实时日志时很实用,不改原文件,只修改输出,让日志里的关键字更醒目。面试时如果能主动说“sed 最大的特点是流式处理,所以它很适合在管道中充当文本修改器”,会比单纯背命令更有深度。
sed 的难点其实不在命令本身,而在于“模式空间”这个概念。很多人不理解sed每一行是独立处理的,跨行操作要用N、H这些多行命令。面试题里如果出现“把两行合并成一行”“跨行替换”这类需求,基本都是考察模式空间和保持空间的配合。题库里这类题目偏难,我建议先把单行处理练熟,再研究N和H,因为大多数实际需求用单行操作就能解决。
4. awk 高频题组:文本处理的“重武器”
4.1 字段分割与内置变量
awk 的处理模型是“按行读入,按列切分”。这个模型是理解 awk 一切特性的基础,也是它和 grep、sed 最本质的区别。
题 22:打印 /etc/passwd 中每个用户的用户名和登录 shell,分隔符是冒号。
awk -F: '{print $1, $7}' /etc/passwd-F:指定冒号作为字段分隔符,$1是第一个字段,$7是第七个字段。看起来简单,但背后的机制值得展开:awk 默认分隔符是空白(空格或 Tab),-F可以临时指定。如果你在脚本里需要频繁切换分隔符,也可以在 BEGIN 块里设置:
awk 'BEGIN{FS=":"} {print $1, $7}' /etc/passwd这两种写法等价,但BEGIN{FS=":"}更灵活,因为它只是赋值给内置变量FS,你甚至可以在处理过程中改变 FS 的值,让不同的行用不同的分隔符解析。
再嵌套一题:
题 29:统计当前目录下所有 .log 文件的总行数。
一个常见解法是:
cat *.log | wc -l但 awk 也能做,而且能统计得更细:
awk '{count++} END{print count}' *.logcount++每读一行执行一次,END块在文件全部处理完后执行。count是没有预先声明的变量,awk 默认把它当数字用,初始值是 0。这种“未声明直接使用”的特性,使用起来很方便,但也容易踩坑——如果你拼错变量名,awk 不会报错,只会默默给你一个错误结果。
4.2 条件、循环与数组统计
awk 的真正威力在于它是一门“微型语言”。题库里这道题是绝对的高频:
题 48:统计 access.log 中每个 IP 的出现次数,并按次数降序输出前 10 个。
awk '{count[$1]++} END{for (ip in count) print count[ip], ip}' access.log | sort -rn | head -10很多人会背这条命令,但面试官更想听你解释三个阶段:读入阶段逐行执行{count[$1]++},用 IP 作为数组下标累加计数;文件读完进入 END 阶段,用for (ip in count)遍历数组;输出结果后交给sort -rn排序,head -10取前 10。count[$1]++这种写法是 awk 做频次统计的核心模式,可以套用到几乎所有分组统计场景,比如按状态码统计、按用户统计、按时间分钟统计等。
再进阶一点:
题 55:求第二列数值的平均值,保留两位小数。
awk '{sum+=$2; n++} END{printf "%.2f\n", sum/n}' data.txtprintf是 awk 里格式化输出的函数,%.2f表示保留两位小数。如果数据文件是空的,n 等于 0,直接sum/n会得到一个无穷大或报错,健壮性好的写法应该加上n>0的判断,这是大数据量脚本里容易忽视的问题。
awk 的数组有一点和普通语言不同:它的下标可以是字符串,本质上是一个关联数组。这个特性让它做分组统计特别自然。比如统计每种状态码的占比:
awk '{code[$9]++} END{total=0; for (c in code) total+=code[c]; for (c in code) printf "%s %.2f%%\n", c, code[c]/total*100}' access.log这种题目已经属于高级范畴,考察的不只是 awk 语法,还有你设计统计流程的思路。建议在纸上画一画:先累加,再遍历,再计算百分比,思路清楚了代码自然就出来了。
4.3 多文件处理与 shell 变量传参
awk 可以一次处理多个文件,而且内置变量FNR和NR的区别是面试必问题:
题 60:有两个文件 a.txt 和 b.txt,希望分别打印各自的行号。
awk '{print FILENAME, FNR, $0}' a.txt b.txtFNR是当前文件内的行号,NR是所有已读入行的总行号。如果文件 A 有 5 行,文件 B 有 3 行,读到 B 的第一行时,FNR是 1,NR是 6。这个区别在合并多个文件做去重时特别重要。
再一个常见场景是 awk 如何接收 shell 变量:
题 68:用 shell 变量 name 作为 awk 的匹配条件,打印包含该变量的行。
name="Alice" awk -v n="$name" '$0 ~ n' file.txt-v选项可以把外部变量传入 awk,在 awk 内部使用n来引用。如果不加-v,直接写成awk '$0 ~ $name',awk 会把$name理解成第 name 个字段,这是完全不同的含义。还有一个陷阱:如果变量名是FS或OFS这种内置变量名,用-v传入会改变 awk 的内置行为,需要特别注意。
awk 的坑多在细节。$0表示整行,$NF表示最后一个字段,这两个符号也经常在题里出现。NF是当前行的字段数量,$NF是取值,两者差一个美元符,含义天差地别。面试时如果把$NF说成“字段数量”,基本就凉了。
5. 综合高频题与易错点:三剑客的组合艺术
5.1 三剑客各司其职的经典套路
实际工作中三剑客很少单独出现,组合使用才是常态。有一道经典综合题:
题 82:从 Nginx 日志中找出返回状态码为 502 的请求,提取出对应的 URL,并统计每个 URL 出现的次数。
这道题需要三个工具协作完成:
grep " 502 " access.log | awk '{print $7}' | sort | uniq -c | sort -rngrep先过滤出包含 502 的行,awk '{print $7}'提取第 7 列(Nginx 日志默认格式里$7是请求 URL),sort | uniq -c统计每个 URL 的次数,再sort -rn排序。这条命令是“先筛、后取、再统计”思路的典型代表,四个阶段各自由最合适的工具完成。
更复杂的场景是 grep 和 awk 配合做条件过滤。比如筛选出访问次数超过 100 的 IP:
awk '{count[$1]++} END{for (ip in count) if (count[ip]>100) print count[ip], ip}' access.log | sort -rn这个需求 grep 无法单独完成,因为“次数超过 100”是一个聚合条件,需要 awk 先把数据算出来,再用if判断。这种多阶段处理思路,比单条命令背诵重要得多。
5.2 面试官最爱挖的坑
三剑客的面试题里有很多隐蔽的坑,我总结几个高频出现、学员错误率最高的:
第一个坑是grep -c到底统计什么。grep -c统计的是匹配的行数,而不是匹配的次数。如果一行里出现了 5 次ERROR,grep -c ERROR只会计数 1。想统计出现总次数,要用grep -o ERROR | wc -l,-o把每次匹配单独输出一行,再数行数。
第二个坑是 sed 的&符号。在 sed 的替换内容里,&表示“整个匹配到的内容”,所以sed 's/foo/(&)/g'会把所有foo替换成(foo)。如果你确实想替换成字面意义的&,需要写成\&,很多人在替换 XML 实体或包含连接符的文本时踩过这个坑。
第三个坑是 awk 的print和printf混用。print是简化输出,字段之间用OFS(默认空格)连接,自动换行;printf是完全格式化输出,不会自动换行。两者混用时输出会粘在一起,非常容易出错。
第四个坑是正则里的贪婪匹配与非贪婪匹配。grep 和 sed 默认都是贪婪匹配,sed 's/<.*>//g'会把一整段标签内容全部删掉,而不是只删第一个标签。Perl 风格的非贪婪在 grep-P里可以用.*?,但 BRE/ERE 本身没有非贪婪的概念。这个点经常出现在需要清洗 HTML 或配置模板的面试题里。
三剑客组合还有一条通用经验:能用管道解决的问题,不要写复杂的单条命令;反之亦然。管道让数据流清晰可见,每一步都能单独验证,调试成本低很多。我在生产环境里排查问题,基本都是一条一条命令串起来,每步用head看几条输出,确认无误再拼成完整管道。
6. 常见问题与排查技巧实录
6.1 高频报错与执行结果异常
把我在教学和实际工作中遇到的高频问题整理成了一张速查表:
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
sed: -e expression #1, char X: unknown option to 's' | 替换内容中包含了定界符(如/) | 更换定界符,如s#old#new#g |
grep: invalid option -- 'r' | 部分环境 grep 版本或别名导致参数解析异常 | 用grep --help查看当前版本支持参数,确认命令格式 |
| awk 输出为空 | $n中的 n 超出了字段数量范围 | 先用awk '{print NF}' file打印每行字段数 |
| awk 统计结果明显偏大 | count[$1]++的下标字段选错,比如$1不是预期的 IP | 先awk '{print $1}' file | head查看字段内容 |
sed 使用-i后文件权限变化 | 部分 sed 实现是通过重写新文件来完成的 | 修改后检查属主和权限,必要时用chmod恢复 |
| grep 匹配中文内容失败 | 系统 locale 未设置为 UTF-8 | 检查LANG环境变量,临时用LANG=en_US.UTF-8 |
这张表里的问题都是我实际踩过或者学员问过的。第一个“未知选项”几乎每天都能在群里看到,原因极其简单,就是替换内容里有/,但多数人第一反应是“sed 是不是坏了”。所以我把这个排最前面。
6.2 几条压箱底的调试经验
最后分享几个让我少走弯路的经验,都是文档里不会写的东西。
第一,调试时永远先不加-i。无论 sed 还是其他修改文件的命令,先让结果输出到终端,肉眼确认后再加上-i。很多人在-i后才发现替换结果不对,原文件已经改坏了,只能从备份里恢复。如果有备份还好,没有备份就只能干瞪眼。
第二,awk 脚本从简单开始,逐步加条件。我见过太多人一上来就写一个庞大的 awk 脚本,报错了却不知道是哪一步出了问题。正确的做法是:先用awk '{print $1}'看看字段对不对,再加匹配条件,再加上统计逻辑,最后加格式化输出。每一步都验证,比整体调试高效得多。
第三,用好head和管道配合。处理超大日志文件时,别一上来就全量跑,先用head -100 file | 你要调试的命令,确认逻辑没问题再全量执行。这个习惯能帮你省下大量等待时间,也能避免一条错误命令在全量文件上产生毁灭性影响。
第四,grep、sed、awk 不是互相替代的关系。很多人在掌握了 awk 之后,恨不得所有文本处理都用 awk 搞定。实际上,能一条 grep 解决的事情就不要写一段 awk,能一条 sed 完成的事就不要引入数组。命令越简单,出错概率越小,别人接手也更容易看懂。这一点我觉得比掌握所有高级语法更重要。
第五,面试时如果被问到不会的题目,不要直接说不会。把你能想到的方向说清楚,比如“这个需求我会用 awk 的数组来统计,但是具体输出格式我需要再确认一下”,面试官至少知道你有解题思路。三剑客本质上都是工具,思路清晰的人就算某个参数记不准,看一眼 man page 也能写出来;思路混乱的人就算背了一百道题,碰到新场景还是懵。这也是我整理这套题库时最强调的部分。
这套 100 道题覆盖的知识点足够应对绝大多数 Linux 岗位面试了,但请你记住,背答案只是底线,能把每条命令拆开讲清楚为什么,才是真正的分水岭。希望你刷完题后,不只是“见过”,而是真正“会用”。