bzip2 命令详解与参数实测(Ubuntu 22.04)

bzip2 块排序压缩器全参数实测:默认级别 9 与 BZh9 文件头解析、-1~-9 块大小级别的倒挂现象、bunzip2 -z 反转默认动作、-t 损坏识别与 bzip2recover、-f 解压尾部垃圾的静默损坏路径实测、-v 统计与组合串限制、-s 小内存模式、无 -r 的目录差异及与 gzip/xz 横向对比

Linux命令Linux系统UbuntuUbuntu 22.04命令行压缩归档2026/10/16

bzip2 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 bzip2 块排序压缩器的实战用法——默认级别 9 的原位压缩与元数据保留、BZh9 文件头逐字节解析、-1~-9 块大小级别的实测耗时与体积(含高级别反而更大的倒挂现象)、-z 让 bunzip2 也能压缩、-t 完整性测试与 bzip2recover 提示、尾部垃圾数据的 -f 强制解压隐患(实测静默损坏路径)、-v 统计行与组合串限制、-s 小内存模式、无 -r 的目录处理差异,以及与 gzip、xz 的横向对比,全部命令与回显已在 Ubuntu 22.04(bzip2 1.0.8)实测通过。

一、环境信息

本节列出本文所有实测所依赖的环境与测试数据,便于读者按同规格复现。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • bzip2 版本:bzip2 1.0.8(13-Jul-2019,bzip2 --version 实测)
  • Ubuntu 22.04 默认不一定预装 bzip2;未预装时执行 apt install bzip2 即可(实测环境即经此安装,包版本 1.0.8)
  • 同族命令:bunzip2(解压)、bzcat(解压到标准输出)、bzgrep(压缩文件上 grep)、bzip2recover(损坏块恢复),随 bzip2 一并提供
  • 演示目录:/root/demo/bzip2/
  • 测试数据:log.txt(seq 1 500000 生成,3,388,895 字节)、rand.bin(/dev/urandom 取 1 MiB,1,048,576 字节,不可压数据代表)、ls.bin(/bin/ls 副本,138,216 字节,真实可执行程序代表)

二、命令概览

2.1 语法与测试数据准备

bzip2 的基本用法是 bzip2 [flags and input files in any order]:默认动作是压缩(-z),以 -d 进入解压模式;不指定文件时从标准输入读到标准输出。与 gzip 相同的是「原位替换、默认删源」;最大的两点差异是——默认压缩级别是 9 而非 6,且级别 -1-9 决定的是块大小(100k900k),直接影响解压所需的内存上限。

💡 技术拓展:bzip2 使用 BWT(Burrows–Wheeler 变换)+ MTF + 哈夫曼的组合算法,对文本类数据的压缩率通常显著优于 gzip 的 DEFLATE,但耗时也更长;与 gzip、xz 的横向实测对比见 4.4 节。bzip2 以「块」为单位压缩,这一特性让损坏文件的局部恢复(bzip2recover)成为可能。

先准备演示数据(三类数据分别代表「高重复文本」「随机不可压数据」「真实可执行程序」):

rm -rf /root/demo/bzip2 && mkdir -p /root/demo/bzip2 && cd /root/demo/bzip2
seq 1 500000 > log.txt
head -c 1M /dev/urandom > rand.bin
cp /bin/ls ls.bin

验证文件清单与大小:

$ ls -l
total 4472
-rw-r--r-- 1 root root 3388895 Oct 15 18:25 log.txt
-rwxr-xr-x 1 root root  138216 Oct 15 18:25 ls.bin
-rw-r--r-- 1 root root 1048576 Oct 15 18:25 rand.bin

2.2 --help 全量回显

bzip2 --help 输出用法与全部参数(实测原样保留,可作为参数速查表):

bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.

   usage: bzip2 [flags and input files in any order]

   -h --help           print this message
   -d --decompress     force decompression
   -z --compress       force compression
   -k --keep           keep (don't delete) input files
   -f --force          overwrite existing output files
   -t --test           test compressed file integrity
   -c --stdout         output to standard out
   -q --quiet          suppress noncritical error messages
   -v --verbose        be verbose (a 2nd -v gives more)
   -L --license        display software version & license
   -V --version        display software version & license
   -s --small          use less memory (at most 2500k)
   -1 .. -9            set block size to 100k .. 900k
   --fast              alias for -1
   --best              alias for -9

   If invoked as `bzip2', default action is to compress.
              as `bunzip2',  default action is to decompress.
              as `bzcat', default action is to decompress to stdout.

   If no file names are given, bzip2 compresses or decompresses
   from standard input to standard output.  You can combine
   short flags, so `-v -4' means the same as -v4 or -4v, &c.

帮助说明三点关键信息:默认动作按调用名分流(bzip2 压缩 / bunzip2 解压 / bzcat 解压到标准输出);-1 .. -9 设置的是 100k~900k 的块大小;短选项可以组合(如 -v4),但组合串中每个字符都必须是合法选项(3.10 节有反例实测)。

2.3 版本确认

确认实测环境的 bzip2 版本:

$ bzip2 --version
bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.
   
   Copyright (C) 1996-2019 by Julian Seward.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms set out in the LICENSE file, which is included
   in the bzip2 source distribution.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   LICENSE file for more details.
   

三、参数逐项实测

3.1 默认压缩:原位替换与元数据保留

与 gzip 一样,bzip2 不带参数时压缩并删除原文件,且保留权限与时间戳:

$ touch -d "2026-10-15 09:30:00" log.txt && chmod 740 log.txt && ls -l log.txt
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

$ bzip2 log.txt

$ ls -l log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

$ file log.txt.bz2
log.txt.bz2: bzip2 compressed data, block size = 900k

$ bzip2 -d log.txt.bz2

$ ls -l log.txt
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

解读:log.txt(3,388,895 字节)压成 log.txt.bz2(614,325 字节,约 18.1%),权限 -rwxr----- 与 mtime Oct 15 09:30 完整保留。file 的输出直接点出 bzip2 文件头记录的关键参数——块大小 900k,即默认级别 9(3.2 节用 od 验证)。对比 gzip 同数据集的 1,066,382 字节(31.5%),bzip2 的压缩率优势明显。解压后大小逐字节还原(3,388,895 字节)。

3.2 文件头 BZh 与级别字节

bzip2 文件头前 4 字节是 BZh + 级别数字,用 od 验证:

$ bzip2 -k log.txt

$ od -An -tx1 -N 4 log.txt.bz2
 42 5a 68 39

$ head -c 4 log.txt.bz2 | od -c
0000000   B   Z   h   9
0000004

$ bzip2 -1 -c log.txt > lv1.bz2

$ od -c -N 4 lv1.bz2
0000000   B   Z   h   1
0000004

$ rm -f log.txt.bz2 lv1.bz2

解读:42 5a 68 39 即 ASCII BZh9——BZh 是 bzip2 流的 magic number,第 4 字节 39(字符 9)记录压缩时的块大小级别;-1 压出的文件则是 BZh1。这个字节同时决定了文件头中声明的块大小,与 file 命令显示的 block size = 900k 一致。注意 bzip2 文件头不保存原文件名与时间戳(这点与 gzip 不同),因此不存在 gzip -n 那样的可复现性问题。

3.3 -k 保留原文件

-k(keep)让压缩时保留输入文件:

$ bzip2 -k log.txt

$ ls -l log.txt log.txt.bz2
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt
-rwxr----- 1 root root  614325 Oct 15 09:30 log.txt.bz2

$ rm -f log.txt.bz2

解读:与 gzip -k 语义完全一致——源文件与 .bz2 共存。压缩与解压方向都可用。

3.4 -d 解压与 bunzip2 等价

bzip2 -d 与独立命令 bunzip2 完全等价:

$ bzip2 log.txt

$ ls -l log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

$ bzip2 -d log.txt.bz2

$ ls -l log.txt
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

$ bzip2 log.txt

$ bunzip2 log.txt.bz2

$ ls -l log.txt
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

$ wc -l log.txt && tail -2 log.txt
500000 log.txt
499999
500000

解读:两种方式解压结果一致(500000 行,末两行 499999/500000)。bunzip2 就是默认进入解压模式的 bzip2;.bz2 是标准后缀,非此后缀的解压识别规则见 3.8 节与 3.9 节。

3.5 压缩级别 -1~-9(默认 9)

bzip2 的级别决定块大小:-1 = 100k 块 … -9 = 900k 块,默认 -9。先确认默认级别等价于 -9:

$ bzip2 -9 -k log.txt

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

逐级实测(-k 保留源文件,每级压完即删 .bz2):

$ time bzip2 -1 -k log.txt
0.079 s

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 534735 Oct 15 09:30 log.txt.bz2

$ time bzip2 -6 -k log.txt
0.090 s

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 570447 Oct 15 09:30 log.txt.bz2

$ time bzip2 -9 -k log.txt
0.089 s

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

$ time bzip2 --fast -k log.txt
0.078 s

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 534735 Oct 15 09:30 log.txt.bz2

$ time bzip2 --best -k log.txt
0.088 s

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

十级全量汇总(3,388,895 字节的 log.txt,实测):

级别(块大小)耗时产物大小(字节)
-1(100k)0.079 s534,735(全场最小)
-2(200k)0.078 s554,114
-3(300k)0.078 s552,624
-4(400k)0.078 s553,708
-5(500k)0.092 s554,126
-6(600k)0.090 s570,447
-7(700k)0.090 s554,486
-8(800k)0.084 s597,386
-9(900k,默认)0.089 s614,325(全场最大)
--fast(即 -1)0.078 s534,735
--best(即 -9)0.088 s614,325

解读与结论:

  • --fast/--best 与 -1/-9 的产物完全一致,是纯别名。
  • 本数据集出现显著的倒挂现象:级别越高产物越大,-9(614,325)比 -1(534,735)大 14.9%。级别本质是块大小,块越大单块覆盖的数据越多,BWT 变换与哈夫曼编码对「高度规则、自相似」的 seq 数据反而不如小块适配得紧凑。
  • 各级别耗时都在 0.078~0.092 s 之间,差距很小——bzip2 的耗时主要由 BWT 排序决定,块大的单块耗时与块多的累计耗时大致相抵。
  • 因此对 bzip2 而言,「--best 就是最好」同样是想当然;此外级别还决定了解压端内存需求(-1 需约 1.1 MB,-9 需约 37 MB 量级),低内存设备解压大级别文件会失败,需 -s(3.11 节)。

💡 要点:gzip 的级别是「搜索深度」、bzip2 的级别是「块大小」、xz 的级别是「预设参数组合」——三者同名参数的机制完全不同,不可迁移类比。无论哪家,「高级别=更小」都必须实测验证。

3.6 -z 强制压缩:bunzip2 也能压

-z 显式指定压缩动作,配合不同调用名可以反转默认行为——bunzip2 -z 也能压缩:

$ bunzip2 -z log.txt; echo rc=$?
rc=0

$ ls -l log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

$ bzip2 -d log.txt.bz2

$ ls -l log.txt
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

$ bzip2 -z -k log.txt; echo rc=$?
rc=0

$ ls -l log.txt log.txt.bz2
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt
-rwxr----- 1 root root  614325 Oct 15 09:30 log.txt.bz2

$ rm -f log.txt.bz2

解读:bunzip2 -z log.txt 成功把 log.txt 压成 .bz2——动作由 -z 决定,调用名只影响无 -z/-d 时的默认值。bzip2 -z -k 是显式压缩 + 保留源文件的组合。写脚本时显式带 -z/-d 可以避免对调用名的隐式依赖。

3.7 -c 输出到标准输出

-c 把压缩或解压结果写到标准输出,不改动输入文件:

$ bzip2 -c log.txt > stdout.bz2

$ ls -l log.txt stdout.bz2
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt
-rw-r--r-- 1 root root  614325 Oct 15 18:25 stdout.bz2

$ bzip2 -dc stdout.bz2 | tail -2
499999
500000

$ rm -f stdout.bz2

解读:bzip2 -c log.txt > stdout.bz2 产物与原位压缩等大(614,325 字节),log.txt 原样保留;-dc 把解压结果接入管道。-c 路径的输出完全由重定向目标决定,也是 3.9 节规避 -f 隐患的推荐用法。

3.8 -t 完整性测试与损坏识别

-t 解压校验但不写出文件:

$ bzip2 -k log.txt

$ bzip2 -t log.txt.bz2; echo rc=$?
rc=0

$ bzip2 -tv log.txt.bz2
  log.txt.bz2: ok

制造截断损坏(保留前 200,000 字节,约为完整文件的 1/3):

$ head -c 200000 log.txt.bz2 > broken.bz2

$ ls -l log.txt.bz2 broken.bz2
-rw-r--r-- 1 root root 200000 Oct 15 18:25 broken.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

$ bzip2 -t broken.bz2; echo rc=$?
bzip2: broken.bz2: file ends unexpectedly

You can use the `bzip2recover' program to attempt to recover
data from undamaged sections of corrupted files.

rc=2

解读:完好文件 -tv 显示 ok、退出码 0;截断文件报 file ends unexpectedly、退出码 2,并提示可用 bzip2recover 从未损坏的块中抢救数据——这正是 bzip2 按块压缩带来的独有能力(gzip 损坏中间部分则整体报废)。尾部垃圾的行为与 -f 的叠加效应见 3.9 节。

3.9 尾部垃圾与 -f 强制解压的隐患(重点实测)

在完好的 .bz2 尾部追加垃圾字节,先看默认解压行为:

$ cp log.txt.bz2 garbage.bz2 && printf "trailing junk" >> garbage.bz2

$ bzip2 -d -k garbage.bz2; echo rc=$?

bzip2: garbage.bz2: trailing garbage after EOF ignored
rc=0

$ bzip2 -q -d -f garbage.bz2; echo rc=$?
rc=0

$ ls -l garbage log.txt
-rwxr----- 1 root root 4003233 Oct 15 18:25 garbage
-rwxr----- 1 root root 3388895 Oct 15 09:30 log.txt

解读:第一次 bzip2 -d -k 行为正常——解压出 3,388,895 字节正确内容,告警 trailing garbage after EOF ignored,退出码 0。但注意最后的 garbage 是 4,003,233 字节——第二条 -q -d -f 命令执行后产物变大了。追加实验(S17 组)定位了这条隐患的确切规律:

$ rm -f garbage garbage.bz2 && cp log.txt.bz2 garbage.bz2 && printf "trailing junk" >> garbage.bz2 && bzip2 -d -k garbage.bz2 && bzip2 -q -d -f -k garbage.bz2 && ls -l garbage garbage.bz2 && dd if=garbage bs=1 skip=3388895 count=8 2>/dev/null | od -An -c && cmp <(tail -c 614338 garbage) garbage.bz2 && echo TAIL_EQ_INPUT_WHOLE || echo TAIL_DIFFERS

bzip2: garbage.bz2: trailing garbage after EOF ignored
-rwxr----- 1 root root 4003233 Oct 15 18:36 garbage
-rwxr----- 1 root root  614338 Oct 15 18:36 garbage.bz2
   B   Z   h   9   1   A   Y   &
TAIL_EQ_INPUT_WHOLE

解读:cmp 逐字节验证——garbage 前 3,388,895 字节与 log.txt 完全一致,多出的 614,338 字节与整个 garbage.bz2 输入文件逐字节相同(尾部以 BZh9 开头正是输入文件自己的文件头)。即:-f 模式下解压含尾部垃圾的文件,bzip2 会把整个输入文件原样追加到解压内容之后。这不是「输出已存在才覆盖异常」——输出文件不存在时同样发生:

$ rm -f garbage && cp log.txt.bz2 g4.bz2 && printf junk >> g4.bz2 && bzip2 -d -f g4.bz2; echo rc=$?; ls -l g4 2>/dev/null
rc=0
-rwxr----- 1 root root 4003224 Oct 15 18:38 g4

解读:g4.bz2 = 614,325 + 4 字节垃圾 = 614,329 字节,解压产物 4,003,224 字节 = 3,388,895(解压内容)+ 614,329(整个输入文件),同样全程无告警、退出码 0。而不带 -f 的首次解压(上文第一段)产物是正确的 3,388,895 字节——-f 是触发这条损坏路径的关键。

对照组:-c 标准输出路径不受影响,结果始终正确:

$ bzip2 -d -k -c g3.bz2 > out_from_stdin 2>/dev/null; echo pipe_rc=$?; wc -c < out_from_stdin
pipe_rc=0
3388895

结论与建议:

  • 首次 bzip2 -d(无 -f)解压含尾部垃圾的文件:内容正确,rc=0,仅告警——安全。
  • bzip2 -d -f 解压含尾部垃圾的文件:解压内容之后会原样追加整个输入文件,静默损坏,rc=0,无任何提示——无论输出文件此前是否存在。
  • 可靠做法:可疑文件先 bzip2 -t 校验;解压优先用 bzip2 -dc file.bz2 > out(-c 路径实测始终输出正确的 3,388,895 字节);解压后用 cmp/校验和比对原始大小。

💡 要点:这条实测与「-f 只是强制覆盖」的直觉相悖,属于典型的「静默数据损坏」隐患。.bz2 来源不可靠(例如网络传输中断、被二次写入过)时,务必先 -t,或一律走 -dc > file 路径。

3.10 -v 压缩统计与组合串限制

-v 输出压缩统计行:

$ bzip2 -v -k log.txt
  log.txt:  5.516:1,  1.450 bits/byte, 81.87% saved, 3388895 in, 614325 out.

$ ls -l log.txt.bz2 && rm -f log.txt.bz2
-rwxr----- 1 root root 614325 Oct 15 09:30 log.txt.bz2

解读:统计行四个指标——压缩比 5.516:1、平均 1.450 bits/byte、节省 81.87%、输入 3,388,895 字节输出 614,325 字节,与 ls -l 实测大小吻合。帮助里写明「a 2nd -v gives more」,但 -v 的重复必须作为独立参数传递:

$ bzip2 -vvi -c log.txt > vvi.bz2
bzip2: Bad flag `-vvi'
bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.

   usage: bzip2 [flags and input files in any order]

   -h --help           print this message
   -d --decompress     force decompression
   -z --compress       force compression
   -k --keep           keep (don't delete) input files
   -f --force          overwrite existing output files
   -t --test           test compressed file integrity
   -c --stdout         output to standard out
   -q --quiet          suppress noncritical error messages
   -v --verbose        be verbose (a 2nd -v gives more)
   -L --license        display software version & license
   -V --version        display software version & license
   -s --small          use less memory (at most 2500k)
   -1 .. -9            set block size to 100k .. 900k
   --fast              alias for -1
   --best              alias for -9

   If invoked as `bzip2', default action is to compress.
              as `bunzip2',  default action is to decompress.
              as `bzcat', default action is to decompress to stdout.

   If no file names are given, bzip2 compresses or decompresses
   from standard input to standard output.  You can combine
   short flags, so `-v -4' means the same as -v4 or -4v, &c.


$ rm -f vvi.bz2

解读:-vvi 被整体拒绝(Bad flag '-vvi')——组合串中的 i 不是 bzip2 的任何选项,一个非法字符就导致整串失效。合法写法是 bzip2 -vv -c log.txt 或 bzip2 -v -v -c log.txt。

💡 要点:bzip2 的短选项组合是「逐字符识别」,而 gzip 系的 -vvi 会静默忽略未知字符——迁移脚本时这是行为差异点,Bad flag 是 bzip2 在保护你。

3.11 -s 小内存模式

-s 把内存占用限制在约 2500k 以内:

$ bzip2 -s -k log.txt

$ ls -l log.txt.bz2
-rwxr----- 1 root root 554114 Oct 15 09:30 log.txt.bz2

$ rm -f log.txt.bz2

解读:-s 产物 554,114 字节,与 -2(200k 块)的产物完全一致——压缩方向上 -s 实际降低了块大小上限;它的主用途在解压方向:低内存设备解压大级别(如 900k 块)文件时,bunzip2 -s 只需约 2.5 MB 内存(正常解压 -9 文件需要数十 MB),代价是解压变慢。嵌入式设备或受限运行环境处理 .bz2 时优先考虑。

3.12 无 -r:目录处理与 gzip 的差异

bzip2 没有递归选项,-r 直接被当作非法标志拒绝:

$ mkdir -p web/conf && cp log.txt web/a.log && cp rand.bin web/b.bin && cp ls.bin web/conf/c.bin

$ bzip2 -r web
bzip2: Bad flag `-r'
bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.

   usage: bzip2 [flags and input files in any order]

   -h --help           print this message
   -d --decompress     force decompression
   -z --compress       force compression
   -k --keep           keep (don't delete) input files
   -f --force          overwrite existing output files
   -t --test           test compressed file integrity
   -c --stdout         output to standard out
   -q --quiet          suppress noncritical error messages
   -v --verbose        be verbose (a 2nd -v gives more)
   -L --license        display software version & license
   -V --version        display software version & license
   -s --small          use less memory (at most 2500k)
   -1 .. -9            set block size to 100k .. 900k
   --fast              alias for -1
   --best              alias for -9

   If invoked as `bzip2', default action is to compress.
              as `bunzip2',  default action is to decompress.
              as `bzcat', default action is to decompress to stdout.

   If no file names are given, bzip2 compresses or decompresses
   from standard input to standard output.  You can combine
   short flags, so `-v -4' means the same as -v4 or -4v, &c.


$ ls -lR web
web:
total 4340
-rwxr----- 1 root root 3388895 Oct 15 18:25 a.log
-rw-r--r-- 1 root root 1048576 Oct 15 18:25 b.bin
drwxr-xr-x 2 root root    4096 Oct 15 18:25 conf

web/conf:
total 136
-rwxr-xr-x 1 root root 138216 Oct 15 18:25 c.bin

$ bzip2 -dr web
bzip2: Bad flag `-dr'
bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.

   usage: bzip2 [flags and input files in any order]

   -h --help           print this message
   -d --decompress     force decompression
   -z --compress       force compression
   -k --keep           keep (don't delete) input files
   -f --force          overwrite existing output files
   -t --test           test compressed file integrity
   -c --stdout         output to standard out
   -q --quiet          suppress noncritical error messages
   -v --verbose        be verbose (a 2nd -v gives more)
   -L --license        display software version & license
   -V --version        display software version & license
   -s --small          use less memory (at most 2500k)
   -1 .. -9            set block size to 100k .. 900k
   --fast              alias for -1
   --best              alias for -9

   If invoked as `bzip2', default action is to compress.
              as `bunzip2',  default action is to decompress.
              as `bzcat', default action is to decompress to stdout.

   If no file names are given, bzip2 compresses or decompresses
   from standard input to standard output.  You can combine
   short flags, so `-v -4' means the same as -v4 or -4v, &c.


$ rm -rf web

解读:从 gzip 迁移过来最容易踩的坑——bzip2 -r web 不是「递归压缩」而是 Bad flag 报错(-dr 同理),目录内容原封不动。目录逐文件压缩用 find 配合(如 find web -type f -exec bzip2 -k {} +),目录打包归档用 tar cjf(4.3 节)。

直接对目录本身压缩则报另一个错(注意退出码与 gzip 不同):

$ mkdir -p datadir && cp log.txt datadir/

$ bzip2 datadir; echo rc=$?
bzip2: Input file datadir is a directory.
rc=1

$ rm -rf datadir

解读:Input file datadir is a directory. 退出码 1——gzip 对同样输入是「忽略」并返回 2,bzip2 是「出错」并返回 1。写跨格式脚本的退出码分支时要注意这个语义差异。

四、实战示例

4.1 bzcat / bzgrep:不解压直接检索

与 gzip 家族的 zcat/zgrep 对应,bzip2 提供 bzcat/bzgrep:

$ bzip2 -k log.txt

$ bzcat log.txt.bz2 | tail -2
499999
500000

$ bzcat log.txt.bz2 | wc -l
500000

$ bzgrep 49999 log.txt.bz2 | head -3
49999
149999
249999

$ rm -f log.txt.bz2

解读:bzcat 等价于 bzip2 -dc,bzcat | wc -l 确认 500000 行完整;bzgrep 一步完成「解压 + grep」,命中 49999、149999、249999 三行。.bz2 大日志的检索统计直接走 bzcat/bzgrep 管道,不落盘。

4.2 不可压数据的压缩天花板

$ bzip2 -k rand.bin

$ ls -l rand.bin rand.bin.bz2
-rw-r--r-- 1 root root 1048576 Oct 15 18:25 rand.bin
-rw-r--r-- 1 root root 1053624 Oct 15 18:25 rand.bin.bz2

$ rm -f rand.bin.bz2

$ bzip2 -k ls.bin

$ ls -l ls.bin ls.bin.bz2
-rwxr-xr-x 1 root root 138216 Oct 15 18:25 ls.bin
-rwxr-xr-x 1 root root  58358 Oct 15 18:25 ls.bin.bz2

解读:随机数据 rand.bin 压完 1,053,624 字节,比原文件大 5,048 字节——bzip2 对不可压数据块的开销比 gzip(仅 +187 字节)重得多,高熵数据上 bzip2 既慢又更大。真实可执行文件 ls.bin 压到 58,358 字节(42.2%,即节省 57.8%),与 gzip 的 58,723 字节几乎持平。结论:对已知不可压内容,直接存原文件。

4.3 tar 归档配合 bzip2

tar cjf 打包 + bzip2 压缩一体化:

$ mkdir -p proj && cp log.txt rand.bin proj/

$ tar cjf proj.tar.bz2 proj

$ ls -l proj.tar.bz2
-rw-r--r-- 1 root root 1685651 Oct 15 18:26 proj.tar.bz2

$ bzip2 -tv proj.tar.bz2
  proj.tar.bz2: ok

$ tar tjf proj.tar.bz2
proj/
proj/log.txt
proj/rand.bin

$ rm -rf proj proj.tar.bz2

解读:proj.tar.bz2 为 1,685,651 字节,同为「tar 流整体压缩」,对照 4.4 节表格:比 gzip 的 proj.tar.gz(2,119,156 字节)小 20.5%,比 xz 的 proj.tar.xz(1,272,948 字节)大 32.5%。bzip2 -t 可直接校验 tar 包完整性,tar tjf 列清单不解压。

4.4 与 gzip / xz 的横向对比

在同一环境、同一数据集上对比五组参数(-c 重定向输出,time 计时,实测):

数据集gzip -6gzip -9bzip2 -9xz -6xz -9e
log.txt(3,388,895 B 文本)1,066,382 B / 0.071 s1,066,773 B / 0.093 s614,325 B / 0.087 s133,816 B / 0.791 s177,964 B / 1.013 s
rand.bin(1,048,576 B 随机)1,048,763 B / 0.020 s1,048,763 B / 0.021 s1,053,788 B / 0.066 s1,048,688 B / 0.165 s1,048,688 B / 0.205 s
ls.bin(138,216 B 可执行)58,723 B / 0.005 s58,597 B / 0.016 s58,358 B / 0.006 s51,400 B / 0.032 s51,452 B / 0.063 s

结论(基于上表实测值):

  • 压缩率:xz ≪ bzip2 < gzip。log.txt 上 bzip2 -9(614,325 B)是 gzip -6 的 57.6%,xz -6(133,816 B)只有 gzip 的 12.5%。
  • 耗时:bzip2(0.087 s)与 gzip(0.071 s)同量级,xz(0.791 s)慢一个数量级——bzip2 是「接近 gzip 的速度拿到明显更好的压缩率」的平衡点。
  • 不可压数据:bzip2 开销最重(+5,212 B),xz 最轻(+112 B),gzip 居中(+187 B)。
  • ls.bin 上三家差距很小(51,400~58,723 B),真实二进制数据上 bzip2 相对 gzip 的优势主要体现在大文件。
  • 注:rand.bin 各列为当次实验独立生成的随机数据,绝对值不可跨列精算,只看相对规律。
  • 选型建议:交互式高频操作选 gzip;追求体积但不想接受 xz 的耗时选 bzip2;冷数据归档、分发包选 xz。

五、注意事项与常见问题

Q1:bzip2 的默认级别是多少?和 gzip 一样吗? 不同。bzip2 默认 -9(900k 块),gzip 默认 -6。且 bzip2 的级别决定块大小而非搜索深度,3.5 节实测本数据集出现「级别越高越大」的倒挂(-9 比 -1 大 14.9%),--best 并不保证最小。

Q2:bzip2 -r 目录 报 Bad flag 是怎么回事? bzip2 没有递归选项,-r/-dr 是非法标志(3.12 节实测)。逐文件递归用 find 目录 -type f -exec bzip2 -k {} +;整目录归档用 tar cjf。

Q3:解压含尾部垃圾的 .bz2 安全吗? 分两种情况:不带 -f 的首次解压安全(内容正确、仅告警、rc=0);带 -f 时会把整个输入文件原样追加到解压内容之后,静默损坏且 rc=0 无告警(3.9 节实测,输出文件不存在时同样发生)。可靠做法:先 bzip2 -t 校验,解压用 bzip2 -dc file.bz2 > out,解压后比对文件大小。

Q4:.bz2 中间损坏了还能救回一部分吗? 能。bzip2 按块压缩,bzip2 -t 对截断文件报 file ends unexpectedly 时提示使用 bzip2recover——它把损坏文件中完好的块逐个抽出为独立 .bz2(3.8 节)。gzip 无此能力,流中段损坏即整体报废。

Q5:bzip2 -vvi 为什么报 Bad flag? bzip2 的短选项组合逐字符识别,-vvi 里的 i 不是合法选项,整串被拒(3.10 节实测)。要双重详细输出应写 bzip2 -vv 或 -v -v。

Q6:低内存设备解压 .bz2 失败怎么办? 级别越高(块越大)解压内存需求越大,-9 文件解压需数十 MB。用 bunzip2 -s 进入小内存模式(上限约 2500k),代价是变慢;3.11 节实测 -s 在压缩方向等价于更小的块上限。

Q7:如何确认一个 .bz2 是哪个级别压的? file file.bz2 会显示 bzip2 compressed data, block size = 900k(3.1 节实测);或用 od 看文件头第 4 字节(3.2 节:BZh9 的 39 即级别 9)。

六、参考链接