这个命令是干啥的
diff 用来对比两个文件的内容差异。你改了配置,想知道和原来有啥不同;你收到同事的代码,想知道他改了什么;你升级了软件包,发现配置文件变了,这些场景 diff 都是首选。
diff 的输出格式第一次看可能有点晕,但理解了就很直观。它告诉你哪些行需要删除(-)、哪些行需要添加(+),以及上下文是什么。
我最早用 diff 是在公司改 Nginx 配置的时候。改完 nginx.conf 发现网站挂了,赶紧 diff -u nginx.conf.bak nginx.conf 看自己改了啥,一眼就发现少了个分号。自那以后每次改配置前我都会先备份,改完就跑一下 diff 确认变更。
基本用法(3分钟上手)
先准备两个文件:
# 创建文件 old.txt
cat > old.txt << 'EOF'
服务器IP: 192.168.1.10
端口: 8080
日志级别: info
超时时间: 30
EOF
# 创建文件 new.txt(模拟修改后的版本)
cat > new.txt << 'EOF'
服务器IP: 192.168.1.10
端口: 8080
日志级别: debug
超时时间: 60
启用缓存: true
EOF
最简用法
# 直接对比两个文件
diff old.txt new.txt
输出:
3,4c3,5
< 日志级别: info
< 超时时间: 30
---
> 日志级别: debug
> 超时时间: 60
> 启用缓存: true
这输出有点难读。3,4c3,5 的意思是"第3-4行被改成第3-5行"。< 开头的是 old 文件的内容,> 开头的是 new 文件的内容。
统一格式(推荐,最好用的方式)
# -u 输出统一格式,包含上下文
diff -u old.txt new.txt
输出:
--- old.txt
+++ new.txt
@@ -1,4 +1,5 @@
服务器IP: 192.168.1.10
端口: 8080
-日志级别: info
-超时时间: 30
+日志级别: debug
+超时时间: 60
+启用缓存: true
这个格式好懂多了。- 开头的是删掉的行,+ 开头的是新增的行,没符号的是没变的上下文。@@ 行告诉你从哪行开始变了。这就是 git diff 那个格式,也是我日常用的最多的一种。
显示完全相同
# 两个文件相同,diff 就没有输出
diff old.txt old_copy.txt # 啥也不输出
# 可以用 exit code 判断是否相同
diff old.txt new.txt
echo $? # 0 相同,1 不同,2 出错
进阶骚操作
递归对比目录
# 对比两个目录里所有文件
diff -r config_backup/ config/
# 仅显示哪些文件不同,不看具体内容
diff -rq config_backup/ config/
-rq 是我最常用的快速对比方式,"快告诉我这两个目录有哪些文件不一样,别给我看内容"。
输出示例:
Files config/nginx.conf and config_backup/nginx.conf differ
Files config/app.yml and config_backup/app.yml differ
Only in config/: new_feature.conf
忽略空格和空行
# 忽略所有空白差异(行尾空格、缩进变化等)
diff -w old.txt new.txt
# 忽略空行
diff -B old.txt new.txt
# 忽略大小写
diff -i old.txt new.txt
有一次我把 YAML 缩进从2个空格改成了4个空格,diff 输出刷屏了。加了 -w 之后才看出来实际内容其实没变。
结合 patch 命令打补丁
diff 生成差异,patch 应用差异。这个组合在以前没有 git 的年代是标配:
# 生成补丁文件
diff -u old.txt new.txt > upgrade.patch
# 把补丁应用到原文件
patch old.txt < upgrade.patch
# 现在的 old.txt 就和 new.txt 一样了
# 撤销补丁(-R 表示反向应用)
patch -R old.txt < upgrade.patch
我现在偶尔也会用这个来给远程服务器发配置变更,本地改好,生成 patch,ssh 上去打补丁,不用传整个文件。
并排显示
# 并排对比,一目了然
diff -y old.txt new.txt
输出:
服务器IP: 192.168.1.10 服务器IP: 192.168.1.10
端口: 8080 端口: 8080
日志级别: info | 日志级别: debug
超时时间: 30 | 超时时间: 60
> 启用缓存: true
| 表示对应行有变化,> 表示新增行。并排显示的缺点是当文件很长时终端显示不下,需要左右滚动。
控制在上下文中显示几行
# 默认的 -u 显示3行上下文
diff -u old.txt new.txt
# 显示5行上下文
diff -U 5 old.txt new.txt
上下文行数越多,越容易看出修改在文件中的位置。
避坑指南
坑1:diff 认为文件不同,但你肉眼看不出来
通常是因为不可见字符,比如行尾空格、tab vs 空格、换行符格式(Windows 的 CRLF vs Linux 的 LF):
# 先看不可见字符
cat -A file.txt
# ^I 表示 tab,$ 表示行尾
# 用 -w 忽略空白差异再比一次
diff -w file1.txt file2.txt
我之前有个同事说"我明明啥都没改,diff 怎么告诉我文件不一样",排查了半小时发现是他用 Windows 的记事本编辑过的文件,换行符从 LF 变成了 CRLF。
坑2:大文件对比直接卡死
diff 是逐行对比的算法,超大文件(几百 MB)直接 diff 会很慢甚至内存溢出:
# 先看大概的行数
wc -l bigfile.txt
# 用 head/tail 取一部分先比一下
head -1000 bigfile1.txt > tmp1.txt
head -1000 bigfile2.txt > tmp2.txt
diff -u tmp1.txt tmp2.txt
或者用 git diff --no-index,它对大文件处理更好一些:
git diff --no-index bigfile1.txt bigfile2.txt
坑3:二进制的文件不能用 diff
diff 默认会告诉你"Binary files differ",不会显示具体差异。看二进制差异用 xxd 转成 hex 再比,或者用专门的工具比如 Beyond Compare、Meld。
坑4:diff 的排列模式符号要理解
# 三种变化标记
# a = append(追加)
# c = change(修改)
# d = delete(删除)
# 示例
5a7 # 第5行之后追加第7行(从另一个文件)
3c3 # 第3行被修改
4d3 # 第4行被删除了
我在脚本里用 diff 做自动化检测的时候,经常用 grep -E '^[<>]' 来快速提取增删的内容。
实战场景(重点!结合真实运维场景)
场景1:上线前对比配置变更
上线前我一定会做的事:对比新配置和当前运行中的配置有什么不同:
# 备份当前配置
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y%m%d)
# 部署新配置后对比差异
diff -u /etc/nginx/nginx.conf.20260622 /etc/nginx/nginx.conf
有一次迁移服务器,我把 upstream 配错了,就是靠 diff 发现少了个服务器 IP。要是直接 reload 上去,502 就来了。
场景2:检查日志中是否出现新错误
# 对比今天的错误日志和昨天的,找出新增的错误
grep ERROR app.log.2026-06-21 | sort > errors_yesterday.txt
grep ERROR app.log.2026-06-22 | sort > errors_today.txt
diff -u errors_yesterday.txt errors_today.txt | grep '^+' | grep -v '^+++'
这个脚本帮我逮到过一次上线后的隐藏 bug,昨天没有的错误今天出现了,一查发现是新发布的代码抛出来的。
场景3:验证备份脚本是否完整
备份完成后,对比备份前后关键目录的文件差异:
# 备份前记录文件列表
find /data -type f | sort > filelist_before.txt
# 备份后重新记录
find /data -type f | sort > filelist_after.txt
# 对比差异
diff filelist_before.txt filelist_after.txt
如果备份过程应该只读不写,diff 显示有差异说明有程序在备份期间写入了文件。
场景4:对比两个版本的代码
虽然现在都用 git 了,但有些场合 git 不在手边(比如生产环境只有最基本的工具):
# 对比同一个文件的两个版本
diff -u /opt/app-v1.0/config/application.yml /opt/app-v2.0/config/application.yml
# 对比目录递归
diff -rq /opt/app-v1.0/config/ /opt/app-v2.0/config/
场景5:用 vimdiff 做可视化对比
# 在终端里可视化对比,左右分屏
vimdiff old.txt new.txt
# 或者
vim -d old.txt new.txt
vimdiff 里:
- ]c 跳到下一个差异
- [c 跳到上一个差异
- do(diff obtain)把对方的差异拉过来
- dp(diff put)把自己的差异推给对方
- :qa 退出
我个人很喜欢 vimdiff,改完代码看差异的时候比看 diff 输出直观得多。
今日作业
准备好下面两个配置文件:
# 文件A(config_v1.ini)
[database]
host=localhost
port=3306
user=root
password=123456
[redis]
host=localhost
port=6379
# 文件B(config_v2.ini)
[database]
host=10.0.0.1
port=3306
user=admin
password=P@ssw0rd
[redis]
host=10.0.0.1
port=6379
db=0
请完成:
1. 用 diff -u 对比两个文件,分析修改了哪些配置
2. 生成一个 patch 文件,然后用 patch 命令把 A 文件更新成 B 文件的内容
3. 用 vimdiff(如果有 vim)或 diff -y 查看并排对比