Jacky's blog
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)
  • 工具链
  • 开发工具

  • 构建工具

  • 版本控制

  • 容器

  • 调试工具

  • 测试工具

  • CLI 工具

    • tmux
    • oh-my-zsh
    • ripgrep 高性能文本搜索工具使用指南
    • awk
    • sed
    • xargs
    • fzf
    • fd
    • raycast
    • VisiData 终极生存指南(vd)
    • yq
    • direnv
    • ln 与 readlink:创建链接、查看目标和排查失效
    • diff 与 patch:比较文件、目录和处理补丁
      • diff - 文件比较工具
        • 基本语法
        • 常用选项速查
        • 快速上手
        • 实战场景
        • 忽略差异选项
        • 输出格式对比
        • 补丁文件操作
        • 实用脚本
        • 性能优化技巧
        • 与其他工具配合
        • 常见问题
        • 最佳实践
        • 替代工具推荐
      • 退出码、补丁与脚本边界
    • watch 周期监控使用指南
    • osascript
  • 网络工具

  • 编辑器

  • 设计工具

  • 多媒体

  • 包管理

  • 平台工具

  • tools
  • cli_tools
Jacky
2026-10-26
目录

diff 与 patch:比较文件、目录和处理补丁

本文从 Shell 命令手册迁出,集中维护文件差异、补丁和比较脚本;基础入口见 Shell 命令手册。

# diff - 文件比较工具

比较文件或目录的差异,是版本控制和配置管理的重要工具

# 基本语法

diff [选项] 文件1 文件2
diff [选项] 目录1 目录2
1
2

# 常用选项速查

选项 说明 使用场景
-u 统一格式输出(推荐) 代码审查、生成补丁
-c 上下文格式输出 需要更多上下文信息
-q 只显示是否不同 快速检查文件是否相同
-s 显示相同文件 目录比较时查看相同文件
-r 递归比较目录 比较项目目录
-N 将缺失文件视为空 新增/删除文件的对比
-i 忽略大小写 不区分大小写的比较
-w 忽略空白字符 忽略缩进和空格差异
-B 忽略空行 忽略空行变化

# 快速上手

1. 基本文件比较

# 最简单的比较
diff file1.txt file2.txt

# 推荐:统一格式输出(最易读)
diff -u file1.txt file2.txt

# 快速检查:只显示是否不同
diff -q file1.txt file2.txt
1
2
3
4
5
6
7
8

输出示例:

--- file1.txt    2025-01-19 10:00:00
+++ file2.txt    2025-01-19 11:00:00
@@ -1,3 +1,4 @@
 line 1          # 未改变
-old line 2      # 删除的行
+new line 2      # 新增的行
+extra line      # 新增的行
 line 3          # 未改变
1
2
3
4
5
6
7
8

输出符号说明:

符号 含义
- 从 file1 删除的行
+ 在 file2 新增的行
(空格) 未改变的行
--- file1 的文件名和时间
+++ file2 的文件名和时间
@@ 行号范围标识

# 实战场景

场景 1:配置文件管理

# 修改配置前备份
cp nginx.conf nginx.conf.bak

# 修改配置后比较差异
diff -u nginx.conf.bak nginx.conf

# 查看具体改了什么
diff -u nginx.conf.bak nginx.conf | less

# 确认无误后删除备份
rm nginx.conf.bak

# 如果改错了,快速恢复
cp nginx.conf.bak nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14

场景 2:代码审查

# 比较两个版本的代码
diff -u old_version.py new_version.py

# 生成补丁文件供审查
diff -u old_version.py new_version.py > code_review.patch

# 查看补丁内容
cat code_review.patch

# 应用补丁
patch old_version.py < code_review.patch
1
2
3
4
5
6
7
8
9
10
11

场景 3:目录对比

# 递归比较两个项目目录
diff -r project_v1/ project_v2/

# 显示相同和不同的文件
diff -rs project_v1/ project_v2/

# 忽略特定文件类型
diff -r --exclude="*.log" --exclude="node_modules" \
     old_project/ new_project/
1
2
3
4
5
6
7
8
9

场景 4:系统文件监控

# 备份重要系统文件
cp /etc/hosts /etc/hosts.original

# 定期检查是否被修改
diff -q /etc/hosts /etc/hosts.original

# 查看具体修改内容
diff -u /etc/hosts.original /etc/hosts

# 脚本自动监控
if diff -q /etc/hosts /etc/hosts.original > /dev/null; then
    echo "hosts 未变化"
else
    status=$?
    case "$status" in
        1) echo "警告:hosts 文件已被修改!"; diff -u /etc/hosts.original /etc/hosts ;;
        *) printf '比较失败: %s\n' "$status" >&2; exit "$status" ;;
    esac
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

场景 5:数据文件比较

# 比较 CSV 文件(忽略空格)
diff -w data_v1.csv data_v2.csv

# 比较日志文件(忽略空行)
diff -B log_yesterday.txt log_today.txt

# 比较并保存差异
diff -u data_old.csv data_new.csv > data_changes.diff
1
2
3
4
5
6
7
8

# 忽略差异选项

# 忽略大小写
diff -i file1.txt file2.txt

# 忽略所有空白字符(空格、Tab)
diff -w file1.txt file2.txt

# 忽略行尾空白
diff -Z file1.txt file2.txt

# 忽略空行
diff -B file1.txt file2.txt

# 组合使用:忽略大小写和空白
diff -iw file1.txt file2.txt

# 忽略匹配特定模式的行
diff -I '^#' file1.txt file2.txt  # 忽略注释行
diff -I '^$' file1.txt file2.txt  # 忽略空行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 输出格式对比

1. 默认格式(简单但不直观)

diff file1.txt file2.txt
# 输出:
# 2c2
# < old line
# ---
# > new line
1
2
3
4
5
6

2. 统一格式 -u(推荐,最常用)

diff -u file1.txt file2.txt
# 输出:
# --- file1.txt
# +++ file2.txt
# @@ -1,3 +1,3 @@
#  line1
# -old line
# +new line
#  line3
1
2
3
4
5
6
7
8
9

3. 上下文格式 -c(详细)

diff -c file1.txt file2.txt
# 输出:
# *** file1.txt
# --- file2.txt
# *************** 1,3 ****
#   line1
# ! old line
#   line3
# --- 1,3 ----
#   line1
# ! new line
#   line3
1
2
3
4
5
6
7
8
9
10
11
12

4. 并排格式 -y(直观对比)

diff -y file1.txt file2.txt
# 输出:
# line1          line1
# old line     | new line
# line3          line3
1
2
3
4
5

# 补丁文件操作

生成补丁:

# 生成单文件补丁
diff -u original.py modified.py > changes.patch

# 生成目录补丁
diff -Naur old_project/ new_project/ > project.patch

# 带描述信息的补丁
# 说明放在真正的 ---/+++ 文件头之前,不自造空文件名补丁头。
cat > feature.patch << 'EOF'
修复:登录页面验证错误

详细说明:
1. 修复邮箱验证正则表达式
2. 添加密码强度检查
EOF
diff -u old.py new.py >> feature.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

应用补丁:

# 查看补丁内容
cat changes.patch

# 预览补丁效果(不实际应用)
patch --dry-run < changes.patch

# 应用补丁
patch original.py < changes.patch

# 应用目录补丁
patch -p1 < project.patch

# 撤销补丁
patch -R original.py < changes.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 实用脚本

配置文件监控脚本:

#!/bin/bash
# config_monitor.sh - 监控配置文件变化

CONFIG="/etc/nginx/nginx.conf"
BACKUP="${CONFIG}.backup"

# 检查备份是否存在
if [ ! -f "$BACKUP" ]; then
    echo "创建初始备份..."
    cp "$CONFIG" "$BACKUP" || exit 1
    exit 0
fi

# diff 的 1 表示不同,2 表示执行错误。I/O 错误时立即停,不进入恢复。
if diff -q "$BACKUP" "$CONFIG" > /dev/null; then
    echo "✅ 配置文件未变化"
else
    status=$?
    [ "$status" -eq 1 ] || { printf '比较失败: %s\n' "$status" >&2; exit "$status"; }
    echo "⚠️ 配置文件已变化,差异详情:"
    if diff -u "$BACKUP" "$CONFIG"; then :; else
        status=$?
        [ "$status" -eq 1 ] || exit "$status"
    fi
    read -r -p "接受新配置填 y;恢复备份填 r;其他输入不修改: " reply || exit 1
    case "$reply" in
        y|Y) cp "$CONFIG" "$BACKUP" || exit 1; echo "✅ 备份已更新" ;;
        r|R) cp "$BACKUP" "$CONFIG" || exit 1; echo "↩️ 已恢复原配置" ;;
        *) echo "未修改文件" ;;
    esac
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

批量文件对比脚本:

#!/bin/bash
# compare_dirs.sh - 比较两个目录并生成报告

if [ "$#" -ne 2 ]; then
    echo "用法: $0 <目录1> <目录2>" >&2
    exit 2
fi
DIR1="$1"
DIR2="$2"
REPORT=$(mktemp "./diff_report_$(date +%Y%m%d_%H%M%S).XXXXXX") || exit 1
SUMMARY=$(mktemp) || exit 1
DETAIL=$(mktemp) || { rm -f "$SUMMARY"; exit 1; }
trap 'rm -f "$SUMMARY" "$DETAIL"' EXIT

# 分别验证比较状态;不把读取失败当作差异报告成功。
if LC_ALL=C diff -rq "$DIR1" "$DIR2" > "$SUMMARY"; then :; else
    status=$?
    [ "$status" -eq 1 ] || exit "$status"
fi
if LC_ALL=C diff -ru "$DIR1" "$DIR2" > "$DETAIL"; then :; else
    status=$?
    [ "$status" -eq 1 ] || exit "$status"
fi
{
    printf '====== 目录比较报告 ======\n时间: %s\n目录1: %s\n目录2: %s\n' "$(date)" "$DIR1" "$DIR2"
    echo "=== 不同的文件 ==="
    grep ' differ$' "$SUMMARY" || :
    echo "=== 仅在目录1存在(含子目录) ==="
    grep -F "Only in $DIR1" "$SUMMARY" || :
    echo "=== 仅在目录2存在(含子目录) ==="
    grep -F "Only in $DIR2" "$SUMMARY" || :
    echo "=== 原始目录摘要(完整保留) ==="
    cat "$SUMMARY"
    echo "=== 详细差异 ==="
    cat "$DETAIL"
} > "$REPORT" || exit 1
printf '报告已生成: %s\n' "$REPORT"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 性能优化技巧

# 只检查是否相同(最快)
diff -q file1 file2

# 大文件比较(限制输出)
diff -u large1.log large2.log | head -100

# 二进制文件比较
diff -a binary1.bin binary2.bin

# 并行比较多个文件
for f in *.txt; do
    diff -q "$f" "backup/$f" &
done
wait
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 与其他工具配合

# 与 git 配合
git diff                    # 查看工作区改动
git diff --cached          # 查看暂存区改动
git diff HEAD~1 HEAD       # 比较两个提交

# 与 vimdiff 配合
vimdiff file1.txt file2.txt  # 可视化对比

# 与 colordiff 配合(彩色输出)
diff -u file1.txt file2.txt | colordiff

# 与 awk 配合统计
diff -u old new | awk '/^@@/ {h=1; next} h && /^\+/ {n++} END {print n+0}'  # 单文件新增行数,跳过 +++ 文件头
diff -u old new | awk '/^@@/ {h=1; next} h && /^-/ {n++} END {print n+0}'   # 单文件删除行数,跳过 --- 文件头
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 常见问题

Q: 为什么推荐使用 -u 选项?

A: 统一格式 (-u) 输出最易读,广泛用于代码审查和生成补丁,是 Git 的默认格式。

Q: 如何比较二进制文件?

A: diff -a 只是强制把二进制输入按文本行处理,可能输出终端控制字符;按字节比较用 cmp,或先用 hexdump 转为可读文本。

cmp file1.bin file2.bin         # 二进制比较
hexdump -C file1.bin > hex1.txt # 转换为十六进制对比
hexdump -C file2.bin > hex2.txt
diff hex1.txt hex2.txt
1
2
3
4

Q: 如何忽略多种类型的差异?

A: 可以组合使用多个选项:

diff -iwB file1 file2  # 同时忽略大小写、空白、空行
1

# 最佳实践

  1. ✅ 总是使用 -u 选项:输出格式最清晰
  2. ✅ 重要操作前先备份:cp file file.bak
  3. ✅ 使用 -q 快速检查:批量比较时提高效率
  4. ✅ 保存差异为补丁:便于审查和应用
  5. ✅ 结合版本控制:Git 已内置强大的 diff 功能
  6. ✅ 编写脚本自动化:定期监控重要文件变化

# 替代工具推荐

工具 特点 使用场景
vimdiff 可视化对比 交互式编辑
colordiff 彩色输出 终端可视化
git diff 版本控制集成 Git 仓库
meld GUI 工具 图形化对比
Beyond Compare 专业工具 商业项目

# 退出码、补丁与脚本边界

工具/情况 状态与处理
diff 0 相同,1 存在差异,2 执行错误(如文件不存在、无读取权限)
cmp 0 相同,1 不同,大于 1 为错误;按工具实现核对
patch --dry-run 先检查补丁是否可应用,成功后再执行真实 patch;备份、路径层级、工作目录一并核对
diff -u … > change.patch 状态 1 通常表示正常生成差异;状态 2 的输出不能作为有效补丁继续使用

if ! diff ... 会把内容不同和读取失败混为一类;严格模式下应放在 if 中显式区分。上文交互监控只在比较确认为差异后提供接受/恢复选项;未确认及读取失败均停止修改。目录报告保留完整原始摘要,分类 grep 只为阅读便利,无法可靠解析任意换行/冒号文件名,不能用于后续自动删改。双重扫描也不是一致性快照,比较期间应避免并发修改。

macOS 自带 diff 的长选项/彩色输出取决于版本;--color、--ignore-trailing-space 等 GNU 扩展需核对本机 diff --help,不支持时用基础 diff -u 或明确安装 GNU diffutils。diff ... | head、统计管道还会涉及正常差异状态 1 和提前截断的 SIGPIPE,不能在严格模式下直接等同普通失败。

依据:GNU diff 退出状态 (opens new window)、GNU cmp (opens new window)、GNU patch (opens new window)。

#Shell#cli#guide
上次更新: 2026/10/26, 22:32:55
ln 与 readlink:创建链接、查看目标和排查失效
watch 周期监控使用指南

← ln 与 readlink:创建链接、查看目标和排查失效 watch 周期监控使用指南→

最近更新
01
Shell 错误处理、调试与资源清理
10-26
02
Shell 执行模型与环境加载
10-26
03
网络工具与诊断:macOS 和 Linux 对照
10-26
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式