8.1 压缩文件的用途与技术

两种压缩原理

  1. 去冗余 bit:存数字 1 只占 1 bit,但文件系统按 Byte(8 bits)分配——7 个 bit 浪费了。压缩算法把这些”空位”挤掉
  2. 去重复:100 个连续的 1 → 存成”100×1”而非真写 100 个 1

压缩文件不能直接被 OS 使用,必须先解压缩。压缩比 = 压缩后/压缩前。

实际压缩效果:Linux 3.10.81 核心源码 570MB → 压缩后仅 76MB 左右(纯文本文件的”多余空间”最多)。现在很多网站传输时自动压缩、客户端再解压——CPU 运算很快,瓶颈在网络带宽。

8.2 Linux 系统常见的压缩指令

扩展名约定(Linux 文件属性不依赖扩展名,这些约定纯粹为了人类识别——不同压缩工具互不兼容):

扩展名工具地位
*.Zcompress已淘汰(gzip 可解,znew 可转格式)
*.gzgzipGNU 出品,最普及
*.bz2bzip2GNU 出品,更高压缩比
*.xzxz压缩比最高,但也最慢
*.tartar 打包不压缩!仅打包
*.tar.gz / *.tgztar + gzip最常见格式
*.tar.bz2tar + bzip2
*.tar.xztar + xz

三大压缩指令只能压缩单个文件——多个文件必须先 tar 打包再压缩,所以 .tar.gz 才是日常标配。

8.2.1 gzip, zcat/zmore/zless/zgrep

gzip 是应用最广的压缩指令,可解 compress、zip 与 gzip 格式。.gz 文件在 Windows 上 WinRAR/7zip 也能解。

gzip [-cdtv#] 文件名
# -c: 输出到 stdout; -d: 解压缩; -t: 检验完整性
# -v: 显示压缩比; -#: 1最快 ~ 9最好(默认6)
# 压缩——原文件消失,变成 file.gz(和 Windows 压缩软件最大的差异!)
gzip -v services     # services: 79.7% -- replaced with services.gz
 
# 解压缩
gzip -d services.gz  # gunzip 等价,但鸟哥建议统一用 gzip -d
 
# 最高压缩比+保留原文件
gzip -9 -c services > services.gz   # -c 输出到 stdout,> 重导向存盘
 
# 不解压直接查看文本
zcat services.gz          # = 解压后 cat
zmore/zless services.gz   # 翻页查看
zgrep -n 'http' services.gz  # 不解压搜索关键词

8.2.2 bzip2, bzcat/bzmore/bzless/bzgrep

bzip2 为取代 gzip 而来,用法几乎一样,多了一个 -k(保留原文件)。压缩比更好(同样文件 gzip 79.7% → bzip2 81.5%),但耗时约 2 倍。

bzip2 [-cdkzv#] 文件名
bzip2 -v services        # 压缩 → services.bz2, 原文件消失
bzip2 -d services.bz2    # 解压缩
bzip2 -k services        # 压缩且保留原文件
bzip2 -9 -c services > services.bz2  # 最高压缩+保留原文件
bzcat/bzmore/bzless services.bz2     # 不解压查看

8.2.3 xz, xzcat/xzmore/xzless/xzgrep

xz 压缩比最高,比 bzip2 更省空间,但耗时暴增 10 倍。-l 可列出压缩前后信息(独有功能)。

xz [-dtlkc#] 文件名
xz -v services        # 压缩 → services.xz(gzip 21% vs xz 15%!)
xz -d services.xz      # 解压缩
xz -k services         # 保留原文件
xz -l services.xz      # 列出压缩前后容量和压缩比
xzcat services.xz      # 不解压查看
 
# 实测(用 time 对比压缩 28MB /etc):
# gzip:  0.019s, 压缩后 7.7MB
# bzip2: 0.042s, 压缩后 6.7MB
# xz:    0.261s, 压缩后 5.5MB

选型:日常用 gzip(快)、归档存长期用 xz(小)、折中用 bzip2。

8.3 打包指令: tar

gzip/bzip2/xz 只能压单个文件。tar(tape archive)将多个文件/目录打包成一个大文件,再加 -z/-j/-J 调用对应压缩。Windows 的 WinRAR 也支持 .tar.gz

8.3.1 tar

tar [-z|-j|-J] [cv] [-f 新文件名] filename...    # 打包+压缩
tar [-z|-j|-J] [tv] [-f 已有tar文件名]            # 查看内容
tar [-z|-j|-J] [xv] [-f 已有tar文件名] [-C 目录]  # 解包
选项含义
-c创建(create);-t 查看(list);-x 解包(extract)——三选一
-zgzip;-j bzip2;-J xz —— 三选一
-v显示处理中的文件名
-f filename指定包文件名——必须紧跟文件名!建议 -f 单独写
-C 目录解压到指定目录
-p保留原本权限与属性(备份必备!)
-P保留绝对路径(危险!一般不推荐)
--exclude=FILE排除指定文件
--newer-mtime=日期只打包指定日期之后变更的文件(差异备份)
# 基本三件套
tar -jcv -f file.tar.bz2 /etc      # 打包+bzip2
tar -jtv -f file.tar.bz2           # 查看包内容
tar -jxv -f file.tar.bz2 -C /tmp   # 解包到 /tmp

-f 的经典坑tar -jcvf file.tar.bz2 正确;tar -jvfc file.tar.bz2 则输出文件名变成 c(因为 -fc)。建议把 -f 单独写。

默认去掉根目录 /——安全设计:打包 /etc 后,包内文件名是 etc/passwd 而非 /etc/passwd。如果保留 /,在 /tmp 解开会直接覆盖系统 /etc!若确实需要保留绝对路径加 -P(大写),但强烈不推荐。

# 实际备份 /etc 对比三种格式
time tar -zpcv -f /root/etc.tar.gz /etc    # gzip: 0.8s, 7.7MB
time tar -jpcv -f /root/etc.tar.bz2 /etc   # bzip2: 1.9s, 6.7MB
time tar -Jpcv -f /root/etc.tar.xz /etc    # xz: 9.0s, 5.5MB
# 注意警告 "Removing leading `/' from member names"

仅解包单一文件:先用 -jtv | grep 找到包内路径 → 用该路径解出(注意不以 / 开头)。

tar -jtv -f /root/etc.tar.bz2 | grep 'shadow'
tar -jxv -f /root/etc.tar.bz2 etc/shadow

排除文件

tar -jcv -f /root/system.tar.bz2 --exclude=/root/etc* \
  --exclude=/root/system.tar.bz2 /etc /root

差异备份(—newer-mtime)

tar -jcv -f new.tar.bz2 --newer-mtime="2015/06/17" /etc/*
# --newer-mtime 只比较 mtime;--newer 同时比较 mtime+ctime
# 未变更的文件最后显示 "file is unchanged; not dumped"

管线打包——不产生中间文件

cd /tmp
tar -cvf - /etc | tar -xvf -
# - 代表 stdout/stdin,通过管线在内存中传递,类似 cp -r

系统备份范例

mkdir /backups; chmod 700 /backups
tar -jcv -f /backups/backup-system-$(date +%Y%m%d).tar.bz2 \
  --exclude=/root/*.gz --exclude=/home/loop* \
  /etc /home /var/spool/mail /var/spool/cron /root

术语:tarfile = 纯打包不压缩(*.tar),tarball = 打包且压缩(*.tar.gz 等)。

tar 还原后 SELinux 陷阱:还原系统文件(如 /etc/shadow)后 SELinux 安全上下文被破坏→可能无法登录。三种修复:① 重启前 restorecon -Rv /etc(最推荐);② 改 /etc/selinux/config → permissive;③ 创建 /.autorelabel 重启。

8.4 XFS 文件系统的备份与还原

tar 针对目录树备份;xfsdump 针对整个文件系统(partition)级别备份。

8.4.1 XFS 文件系统备份 xfsdump

五条限制:① 只能备份已挂载的文件系统;② 需要 root 权限;③ 只能备份 XFS;④ 备份出的数据只能由 xfsrestore 解析;⑤ 靠 UUID 识别,不能备份两个相同 UUID 的文件系统;⑥ 只能备份整个文件系统(如 /boot),不能备份普通目录(如 /etc)

完整备份与累积备份:level 0 = 完整备份;level 1~9 = 累积(增量)备份。level 1 只备份与 level 0 的差异;level 2 只备份与 level 1 的差异。备份记录在 /var/lib/xfsdump/inventory/

# 先确认是独立文件系统
df -h /boot       # → /dev/vda2
 
# level 0 完整备份
xfsdump -l 0 -L boot_all -M boot_all -f /srv/boot.dump /boot
 
# level 1 累积备份(只会备份新增/修改的文件)
dd if=/dev/zero of=/boot/testing.img bs=1M count=10  # 模拟变更
xfsdump -l 1 -L boot_2 -M boot_2 -f /srv/boot.dump1 /boot
# 观察文件大小: boot.dump ≈ 98MB, boot.dump1 ≈ 10MB(正好是新增文件大小!)
 
# 查看所有备份记录
xfsdump -I    # 输出含 session id, level, label, time, pathname

8.4.2 XFS 文件系统还原 xfsrestore

xfsrestore -I                        # 查看备份记录(同 xfsdump -I)
# 完整还原 level 0
xfsrestore -f /srv/boot.dump -L boot_all /boot
# 注意:同名文件被覆盖,但原位置**新增的文件不会被删除**
 
# 还原到其他位置查看
xfsrestore -f /srv/boot.dump -L boot_all /tmp/boot
 
# 只还原包内某一个目录
xfsrestore -f /srv/boot.dump -L boot_all -s grub2 /tmp/boot2
 
# 累积还原:先 level 0 再 level 1
xfsrestore -f /srv/boot.dump -L boot_all /tmp/boot
xfsrestore -f /srv/boot.dump1 /tmp/boot
 
# 互动模式(不知道包里有啥时用 -i 进入交互界面选择文件)
xfsrestore -f /srv/boot.dump -i /tmp/boot3
# 进入后可 ls, cd, add, delete, extract

8.5 光盘写入工具

8.5.1 mkisofs:创建镜像文件

mkisofs [-o 镜像文件] [-Jrv] [-V vol] [-m file] [-graft-point isodir=systemdir ...] 待备份文件...
# -o: 输出 ISO 文件名
# -r: 通过 Rock Ridge 记录 UID/GID 等 Unix 文件信息
# -J: 兼容 Windows 的长文件名(最长 64 Unicode 字符)
# -V: Volume 名称(类似 Windows 光盘标题)
# -m: 排除文件(支持通配符)
# -graft-point: 指定镜像内目录结构

注意:不同目录下有同名文件(如 /root/crontab 和 /etc/crontab)时 mkisofs 会报错——因为光盘的 iso9660 文件系统不允许同名。

# 简单打包——所有文件堆在根目录(不推荐)
mkisofs -r -v -o /tmp/system.img /root /home /etc
 
# 使用 -graft-point 指定镜像内目录结构(推荐)
mkisofs -r -V 'linux_file' -o /tmp/system.img \
  -m /root/etc -graft-point /root=/root /home=/home /etc=/etc
 
# 验证:挂载 ISO 查看
mount -o loop /tmp/system.img /mnt && ls /mnt && umount /mnt

制作可开机光盘:从原版 ISO 提取内容 → 修改 → 重建时加 -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot 等开机参数。详细流程见原书。

8.5.2 cdrecord:光盘烧录工具

CentOS 7 使用 wodim(兼容 cdrecord)。

wodim --devices dev=/dev/sr0                # 侦测烧录机位置
wodim -v dev=/dev/sr0 blank=fast            # 抹除可复写光盘(-v 显示过程)
wodim -v dev=/dev/sr0 speed=4 -eject /tmp/system.img  # 烧录(-eject 完成后自动弹出)
# 非可复写光盘省略 blank 步骤

8.6 其他常见的压缩与备份工具

8.6.1 dd

dd 逐扇区读取,不认文件系统——连未使用扇区也写入,备份文件 = 分区总容量(不管实际用了多少)。优势是任何文件系统格式都能备份/还原。

dd if="input_file" of="output_file" bs="block_size" count="number"
# bs 默认 512 Bytes(一个扇区)
 
# 备份文件
dd if=/etc/passwd of=/tmp/passwd.back
 
# 光盘 → 镜像文件
dd if=/dev/sr0 of=/tmp/system.iso
 
# 镜像 → USB 磁盘(制作启动盘!)
dd if=/tmp/system.iso of=/dev/sda     # 注意:of 是整个磁盘不是分区!
 
# 备份整个分区——文件大小 = 分区总容量
dd if=/dev/vda2 of=/tmp/vda2.img      # 1GB 分区 → 1GB 文件
 
# 完整复制分区(包括 boot sector)
# 先 fdisk 创建相同大小的分区,再 dd 复制

dd 做 USB 启动盘是最经典的用途——比任何工具都快,一行搞定。

8.6.2 cpio

cpio 可以备份任何文件(包括 /dev 下的设备文件),但不会自己找文件——必须配合 find 等指令提供文件列表。

cpio -ovcB  > [file|device]    # 备份(-o=output, -v=verbose, -c=统一格式, -B=加速读写)
cpio -ivcdu < [file|device]    # 还原(-i=input, -d=自动创建目录, -u=覆盖旧文件)
cpio -ivct  < [file|device]    # 查看内容(-t=查看)
# 备份 /boot 下所有文件
cd /
find boot | cpio -ocvB > /tmp/boot.cpio
# 注意:必须 cd 到 / 再 find boot(不能用 find /boot),因为 cpio 不认绝对路径
 
# 查看备份内容
cpio -ivct < /tmp/boot.cpio
 
# 还原
cd /
cpio -idvc < /tmp/boot.cpio
 
# 整盘备份到磁带机
find / | cpio -ocvB > /dev/st0
cpio -idvc < /dev/st0

initramfs(/boot/initramfs-xxx.img)就是用 cpio 打包的!file 检查其类型会显示 “ASCII cpio archive”。

关键命令

命令用途
gzip -v/-d/-c压缩/解压 .gz
zcat/zgrep不解压查看/搜索 .gz
bzip2 -v/-d/-k压缩/解压 .bz2
xz -v/-d/-k/-l压缩/解压 .xz
tar -jcv -fbzip2 打包
tar -jtv -f查看包内容
tar -jxv -f -C解包到指定目录
tar --exclude / --newer-mtime排除/差异备份
xfsdump -l N -L label -fXFS 备份(level 0~9)
xfsdump -I查看备份记录
xfsrestore -f -L -sXFS 还原(-s 指定子目录,-i 互动模式)
mkisofs -r -o -graft-point创建 ISO 镜像
wodim -v dev=/dev/sr0光盘烧录
dd if= of= bs=扇区级复制(做 USB 启动盘)
cpio -ovcB / cpio -idvc配合 find 备份/还原
restorecon -Rv /etc修复 SELinux 上下文