Linux 命令行入门学习资料 day_5
diff命令与命令行自动化比对
一、为什么学diff?—— 比对是调试和测试的核心
在编程学习或项目开发中,我们经常需要做一件事:检查程序的输出是不是和预期的一样。
- 你写了一个 C 程序,运行后输出一段文本,你想知道它和正确答案有没有区别。
- 你修改了代码,想确保修改后的输出和修改前完全一致(回归测试)。
- 你要批改作业或自动判题,需要让计算机自动比较结果。
diff就是 Linux 下用来逐行比较两个文件差异的标准工具。掌握了它,你就拥有了命令行自动化比对的能力。
二、diff是什么?—— 逐行比较的利器
diff是difference的缩写。它的作用是:
给定两个文件,找出它们有哪些行不同,并以一定的格式显示出来。
基本用法:
diff文件1 文件2如果两个文件完全相同,diff不会输出任何东西(静默)。
如果有差异,它会显示哪些行需要修改才能让文件1变成文件2。
三、最简单的例子:两个文本文件的比较
我们创建两个文件来感受一下。
echo"apple">a.txtecho"banana">b.txt然后比较:
diffa.txt b.txt输出:
1c1 < apple --- > banana解读:第一行1c1表示“文件1的第1行改变(c=change)成文件2的第1行”。<开头的是文件1的内容,>开头的是文件2的内容。
如果改成相同内容:
echo"apple">b.txtdiffa.txt b.txt没有任何输出,说明两文件完全相同。
四、把程序输出“抓”出来 —— 重定向与预期结果文件
自动化比对的关键一步是:把程序输出保存到文件里。
你之前学过运行程序用./hello,但输出只是显示在屏幕上。要想让diff比较,必须把它存成一个文件。
方法:输出重定向
./hello>my_output.txt# 将标准输出保存到 my_output.txt(覆盖)./hello>>my_output.txt# 追加到文件末尾预期结果文件:你事先准备好的、正确无误的输出文件,通常叫expected.txt。
比如:
echo"Hello, Linux!">expected.txt然后编译你的程序并运行,把实际输出保存:
gcc hello.c-ohello ./hello>actual.txt用diff比较:
diffexpected.txt actual.txt没有输出,说明程序输出完全符合预期。如果有差异,diff会打印出哪里不一样。
五、自动化比对的基础:diff的退出状态码
在自动化脚本里,我们不能用人眼去看有没有输出,而要通过程序的退出码来判断是否相同。
diff发现文件相同:退出码为 0diff发现文件不同:退出码为 1diff遇到错误(如文件不存在):退出码为 2
查看退出码:
diffexpected.txt actual.txtecho$?# 显示上一条命令的退出码在 Shell 脚本里,我们可以这样用:
ifdiffexpected.txt actual.txt>/dev/null;thenecho"测试通过"elseecho"测试失败"fi> /dev/null是丢弃diff的输出,我们只关心退出码。
六、diff常用选项详解(打造你的比对工具箱)
1.-u—— 统一格式(Unified format)
这是最常用、最易读的格式,也是 Git 等工具使用的格式。
diff-u文件1 文件2输出会显示上下文,-号开头为旧文件行,+号开头为新文件行。
示例:
diff-uexpected.txt actual.txt--- expected.txt 2025-01-01 12:00:00 +++ actual.txt 2025-01-01 12:00:00 @@ -1 +1 @@ -Hello, Linux! +I can use gcc!一目了然:要变成 actual.txt,需要把“Hello, Linux!”替换为“I can use gcc!”。
2.-c—— 上下文格式(Context format)
类似-u,但风格不同,较早使用。
diff-cfile1 file23.-y—— 并排显示(Side by side)
将两个文件的内容左右并排显示,中间用符号指示差异。
diff-yfile1 file2|表示该行有差异<表示只在左边文件存在>表示只在右边文件存在
适合在宽终端下快速扫视。
4.-q—— 只报告是否相同(Quiet)
不显示具体差异,只根据退出码告诉你文件是否不同。
diff-qfile1 file2如果相同,无输出;如果不同,输出Files file1 and file2 differ。
5.-s—— 报告相同(Report identical)
当文件相同时,给出明确消息。常与-q组合用。
diff-sfile1 file2输出:Files file1 and file2 are identical。
6.-w—— 忽略空白差异
有时程序输出多了一些空格或空行,但实质没错。-w会忽略所有空白字符的差异。
diff-wfile1 file27.-B—— 忽略空行差异
忽略只包含空行的增删。
七、自动化比对实战:写一个测试脚本
假设我们的 C 程序add计算两个数的和,我们想自动测试多组数据。
目录结构:
test/ ├── add.c ├── add (编译好的可执行文件) ├── test.sh (自动化测试脚本) ├── tests/ │ ├── 1.in (输入文件) │ ├── 1.out (预期输出) │ ├── 2.in │ └── 2.out测试脚本test.sh示例:
#!/bin/bash# 自动化测试脚本PASS=0FAIL=0foriin12;doecho"Running test$i..."./add<tests/$i.in>tests/$i.actualifdiff-qtests/$i.out tests/$i.actual>/dev/null;thenecho"Test$iPASSED"PASS=$((PASS+1))elseecho"Test$iFAILED"echo"Expected:"cattests/$i.outecho"Got:"cattests/$i.actualFAIL=$((FAIL+1))fidoneecho"========================="echo"Results:$PASSpassed,$FAILfailed"解读:
< tests/$i.in将输入文件的内容重定向给程序(相当于你在键盘输入)。> tests/$i.actual保存实际输出。diff -q静默比较,只看退出码。- 如果失败,我们手动打印预期和实际,方便调试。
运行脚本:
chmod+x test.sh ./test.sh这就实现了一个最简单的自动化判题系统。
八、配合make或gcc进行全自动测试
在 Makefile 里也可以集成测试目标:
test: add @for i in 1 2; do \ ./add < tests/$$i.in > tests/$$i.actual; \ if diff -q tests/$$i.out tests/$$i.actual > /dev/null; then \ echo "Test $$i PASS"; \ else \ echo "Test $$i FAIL"; \ fi; \ done然后执行make test即可一键编译并测试。
九、练习题(分层,动手做)
基础篇
- 创建两个文件
a.txt(内容为“red\ngreen\nblue”)和b.txt(内容为“red\nyellow\nblue”),使用diff查看差异。再使用diff -u和diff -y分别查看,对比输出格式。 - 将
a.txt复制为c.txt,用diff a.txt c.txt验证它们相同,然后观察echo $?的返回值。 - 用
diff -s a.txt c.txt查看输出,理解-s的作用。
进阶篇:程序输出比对
- 编写一个简单的 C 程序
greet.c,输出Hello, World!。- 编译后用
./greet > output.txt保存输出。 - 手动创建
expected.txt,内容为Hello, World!。 - 用
diff output.txt expected.txt判断输出是否正确。 - 故意把
expected.txt内容改成Hello, Linux!,再次运行diff,观察差异。
- 编译后用
- 使用
diff -w比较两个内容相同但有多余空格或空行的文件,验证-w的忽略空白功能。
提高篇:自动化脚本
- 承接练习4,写一个
check.sh脚本,功能:- 编译
greet.c - 运行
./greet > output.txt - 用
diff比较output.txt与expected.txt - 如果相同,打印 “PASS”;否则打印 “FAIL” 并显示差异(用
-u格式)。
- 编译
- 设计一个包含多组测试数据的例子:编写程序
multiply,读取两个整数(从标准输入),输出乘积。- 准备至少 3 组测试输入文件
1.in、2.in、3.in和对应输出文件1.out、2.out、3.out。 - 编写自动化测试脚本
test_multiply.sh,循环运行并报告通过/失败情况,最后统计总数。
- 准备至少 3 组测试输入文件
十、答案与解析
基础篇
diff a.txt b.txt输出2c2之类的变化。diff -u会显示带-和+的统一格式。diff -y左右对照,中间会有|标记差异行。echo $?输出0。diff -s a.txt c.txt输出Files a.txt and c.txt are identical。
进阶篇
- 正确时
diff output.txt expected.txt无输出,$?为 0。改错后 diff 会显示差异。 - 准备两个文件,一个正常,一个行末多了空格或中间多了空行,
diff -w会认为它们相同。
提高篇
check.sh内容参考:#!/bin/bashgcc greet.c-ogreet ./greet>output.txtifdiff-qoutput.txt expected.txt>/dev/null;thenecho"PASS"elseecho"FAIL"diff-uexpected.txt output.txtfi- 输入输出文件示例:
1.in内容:3 5;1.out内容:152.in内容:0 10;2.out内容:0
脚本结构与第七节的test.sh类似,循环变量相应增加。
十一、✅diff命令速查卡
| 选项 | 作用 | 示例 |
|---|---|---|
| (无) | 默认输出差异行 | diff file1 file2 |
-u | 统一格式,最易读 | diff -u file1 file2 |
-y | 并排显示 | diff -y file1 file2 |
-q | 只报告是否不同,不看细节 | diff -q file1 file2 |
-s | 相同时打印消息 | diff -s file1 file2 |
-w | 忽略空白差异 | diff -w file1 file2 |
-B | 忽略空行差异 | diff -B file1 file2 |
-r | 递归比较目录 | diff -r dir1 dir2 |
自动化核心:利用退出码(0为相同,1为不同)在 Shell 脚本中判断。
十二、学习检查清单
diff的基本作用是什么?如果两个文件完全相同,diff会输出什么?- 如何把程序输出保存到文件?需要用到哪个符号?
diff -u和diff -y的输出分别有什么特点?- 在 Shell 脚本中,如何判断
diff发现文件不同?利用什么变量? - 编写自动化测试脚本的一般步骤是什么?(编译、运行、重定向输出、比较、报告)
- 如果程序输出多了一些无害的空格,使用哪个选项可以让
diff忽略它们?
