Linux 命令行入门学习资料 day_5

diff命令与命令行自动化比对


一、为什么学diff?—— 比对是调试和测试的核心

在编程学习或项目开发中,我们经常需要做一件事:检查程序的输出是不是和预期的一样

  • 你写了一个 C 程序,运行后输出一段文本,你想知道它和正确答案有没有区别。
  • 你修改了代码,想确保修改后的输出和修改前完全一致(回归测试)。
  • 你要批改作业或自动判题,需要让计算机自动比较结果。

diff就是 Linux 下用来逐行比较两个文件差异的标准工具。掌握了它,你就拥有了命令行自动化比对的能力。


二、diff是什么?—— 逐行比较的利器

diffdifference的缩写。它的作用是:

给定两个文件,找出它们有哪些行不同,并以一定的格式显示出来。

基本用法

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发现文件相同:退出码为 0
  • diff发现文件不同:退出码为 1
  • diff遇到错误(如文件不存在):退出码为 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 file2

3.-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 file2

7.-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

这就实现了一个最简单的自动化判题系统


八、配合makegcc进行全自动测试

在 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即可一键编译并测试。


九、练习题(分层,动手做)

基础篇

  1. 创建两个文件a.txt(内容为“red\ngreen\nblue”)和b.txt(内容为“red\nyellow\nblue”),使用diff查看差异。再使用diff -udiff -y分别查看,对比输出格式。
  2. a.txt复制为c.txt,用diff a.txt c.txt验证它们相同,然后观察echo $?的返回值。
  3. diff -s a.txt c.txt查看输出,理解-s的作用。

进阶篇:程序输出比对

  1. 编写一个简单的 C 程序greet.c,输出Hello, World!
    • 编译后用./greet > output.txt保存输出。
    • 手动创建expected.txt,内容为Hello, World!
    • diff output.txt expected.txt判断输出是否正确。
    • 故意把expected.txt内容改成Hello, Linux!,再次运行diff,观察差异。
  2. 使用diff -w比较两个内容相同但有多余空格或空行的文件,验证-w的忽略空白功能。

提高篇:自动化脚本

  1. 承接练习4,写一个check.sh脚本,功能:
    • 编译greet.c
    • 运行./greet > output.txt
    • diff比较output.txtexpected.txt
    • 如果相同,打印 “PASS”;否则打印 “FAIL” 并显示差异(用-u格式)。
  2. 设计一个包含多组测试数据的例子:编写程序multiply,读取两个整数(从标准输入),输出乘积。
    • 准备至少 3 组测试输入文件1.in2.in3.in和对应输出文件1.out2.out3.out
    • 编写自动化测试脚本test_multiply.sh,循环运行并报告通过/失败情况,最后统计总数。

十、答案与解析

基础篇

  1. diff a.txt b.txt输出2c2之类的变化。diff -u会显示带-+的统一格式。diff -y左右对照,中间会有|标记差异行。
  2. echo $?输出0
  3. diff -s a.txt c.txt输出Files a.txt and c.txt are identical

进阶篇

  1. 正确时diff output.txt expected.txt无输出,$?为 0。改错后 diff 会显示差异。
  2. 准备两个文件,一个正常,一个行末多了空格或中间多了空行,diff -w会认为它们相同。

提高篇

  1. 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
  2. 输入输出文件示例:
    1.in内容:3 51.out内容:15
    2.in内容:0 102.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 脚本中判断。


十二、学习检查清单

  1. diff的基本作用是什么?如果两个文件完全相同,diff会输出什么?
  2. 如何把程序输出保存到文件?需要用到哪个符号?
  3. diff -udiff -y的输出分别有什么特点?
  4. 在 Shell 脚本中,如何判断diff发现文件不同?利用什么变量?
  5. 编写自动化测试脚本的一般步骤是什么?(编译、运行、重定向输出、比较、报告)
  6. 如果程序输出多了一些无害的空格,使用哪个选项可以让diff忽略它们?