当前位置:首页>Linux>每天学一个Linux命令系列(14):diff - 对比两个配置文件到底差在哪

每天学一个Linux命令系列(14):diff - 对比两个配置文件到底差在哪

  • 2026-09-02 16:28:18
每天学一个Linux命令系列(14):diff - 对比两个配置文件到底差在哪
运
运维少年 · 科技观察

这个命令是干啥的

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 查看并排对比

E N D

运维少年 · 科技观察

最新文章

随机文章