Apache Commons Compress 常用 CLI 命令与工具详解

实测 Commons Compress 1.28.0 命令行用法:发行包 jar 的 Lister 主类、java -cp 启动、jshell 交互实操、javac 手动运行示例,并对照系统 tar、gzip、xz、bzip2 命令。

Apache Commons CompressApache Commons Compress2026/10/13

Apache Commons Compress 常用 CLI 命令与工具详解

目标:搞清楚 Commons Compress 1.28.0 到底能怎么「在命令行用」——发行包 jar 自带的 Lister 入口、jshell 交互实验、javac/java 手动运行示例程序,并与 tar/gzip 等系统命令做对照。全部命令与回显已在 Ubuntu 22.04 / CentOS 8(JDK 17)实测通过。

一、环境信息

本章所有命令行操作基于以下环境。Commons Compress 是 jar 库,无常驻服务,其「命令行能力」只有三种形态:jar 清单声明的 Lister 主类、jshell 交互环境、以及手写的演示程序。

java -version 2>&1 | head -1
tar --version | head -1
gzip --version | head -1
bzip2 --version 2>&1 | head -1
xz --version | head -1

实测回显(Ubuntu 22.04):

openjdk version "17.0.20.1" 2026-08-18
tar (GNU tar) 1.34
gzip 1.10
bzip2, a block-sorting file compressor.  Version 1.0.8, 13-Jul-2019.
xz (XZ Utils) 5.2.5

准备实验文件与发行包(手动安装方式,依赖 jar 缺一不可,原因见第二章):

sudo mkdir -p /opt/compress-lab/lib
cd /opt/compress-lab
sudo curl -fsSLO https://downloads.apache.org/commons/compress/binaries/commons-compress-1.28.0-bin.tar.gz
sudo curl -fsSLO https://downloads.apache.org/commons/compress/binaries/commons-compress-1.28.0-bin.tar.gz.sha512
printf '%s  %s\n' "$(cat commons-compress-1.28.0-bin.tar.gz.sha512)" commons-compress-1.28.0-bin.tar.gz | sha512sum -c -
sudo tar xzf commons-compress-1.28.0-bin.tar.gz
cd lib && sudo curl -fsSLO https://repo1.maven.org/maven2/commons-io/commons-io/2.20.0/commons-io-2.20.0.jar && sudo curl -fsSLO https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.jar && sudo curl -fsSLO https://repo1.maven.org/maven2/commons-codec/commons-codec/1.19.0/commons-codec-1.19.0.jar && cd /opt/compress-lab
mkdir -p demo/data && echo "hello commons compress" > demo/data/a.txt
tar -cf demo/sample.tar -C demo/data a.txt

实测回显(校验一步):

commons-compress-1.28.0-bin.tar.gz: OK

💡 技术拓展:命令行处理压缩文件前要先分清两类工具的边界。**归档(archiver)**把多文件打包成一个文件(tar/zip/jar),**压缩(compressor)**只处理单条数据流(gzip/bzip2/xz);tar.gz 是「先归档再压缩」的两层结构,需要两层工具配合。对比下表:

类别格式是否压缩随机访问典型工具
归档tar否(仅打包)否GNU tar、Commons Compress
归档zip是(每条目 DEFLATE)是(中央目录)Info-ZIP、Commons Compress
归档jar是(zip 超集)是JDK jar、Commons Compress
归档7z是部分p7zip、Commons Compress(只读为主)
压缩gzip是(DEFLATE)否gzip、Commons Compress
压缩bzip2是(BWT)否bzip2、Commons Compress
压缩xz是(LZMA2)否XZ Utils、Commons Compress
压缩zstd是(Zstandard)否zstd、Commons Compress(需 zstd-jni)

本章既讲 Commons Compress 自己的命令行入口,也把它与上表中的系统命令逐一对照。

💡 技术拓展:Java 生态里能做压缩的库不止一家,选库前可参考下表,本文的主角是覆盖面最广的 Commons Compress:

库坐标支持格式特点
JDK 内置无(java.util.zip)zip/gzip/deflate零依赖,但不支持 tar、7z、bzip2、xz 等
Commons Compressorg.apache.commons:commons-compress14 种压缩流、8 种归档流覆盖面最广,API 统一
snappy-javaorg.xerial.snappy:snappy-javasnappy极致速度,JNI 实现,无归档能力
XZ for Javaorg.tukaani:xzxz/lzma高压缩比,Commons Compress 以它为可选依赖

二、发行包自带的命令行入口:Lister

先回答一个问题:commons-compress-1.28.0.jar 能不能像 tar 一样当命令行工具用?答案藏在 jar 清单里。解出清单查看:

cd /opt/compress-lab/commons-compress-1.28.0
jar xf commons-compress-1.28.0.jar META-INF/MANIFEST.MF
grep -E "Main-Class|X-Compile" META-INF/MANIFEST.MF

实测回显:

Main-Class: org.apache.commons.compress.archivers.Lister
X-Compile-Source-JDK: 1.8
X-Compile-Target-JDK: 1.8

清单声明了 Main-Class 为 org.apache.commons.compress.archivers.Lister——一个「列出归档内容」的工具类,同时确认字节码目标是 Java 8。但直接 java -jar 会失败:

cd /opt/compress-lab
java -jar commons-compress-1.28.0/commons-compress-1.28.0.jar
echo "退出码: $?"

实测回显:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/lang3/ArrayUtils
        at org.apache.commons.compress.archivers.Lister.main(Lister.java:80)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.ArrayUtils
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)

(输出节选,实测首尾,省略了最后一条调用帧;退出码 1)

原因:1.28.0 有三个必需依赖(commons-lang3、commons-io、commons-codec),而 java -jar 模式会忽略 -cp。正确的命令行形态是 -cp 带上依赖、用全限定类名启动:

java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister demo/sample.tar

实测回显:

Analyzing demo/sample.tar
Detected format tar
Created org.apache.commons.compress.archivers.tar.TarFile@1698c449
a.txt

四行输出依次是:分析对象、自动识别的格式、内部创建的随机访问类(tar 用 TarFile)、条目列表。对 zip 同理(内部用 ZipFile):

cd demo/data && zip -q ../sample.zip a.txt && cd /opt/compress-lab
java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister demo/sample.zip

实测回显:

Analyzing demo/sample.zip
Detected format zip
Created org.apache.commons.compress.archivers.zip.ZipFile@1c4af82c
a.txt

不带参数运行可以看到它的用法说明:

java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister

实测回显:

Parameters: archive-name [archive-type]

The magic archive-type 'zipfile' prefers ZipFile over ZipArchiveInputStream
The magic archive-type 'tarfile' prefers TarFile over TarArchiveInputStream

第二参数可指定类型(如 zip、tar),帮助里提到的 zipfile、tarfile 两个魔法值实测在 1.28.0 上会报错,属已知行为,指定标准类型名即可:

java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister demo/sample.zip zip
java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister demo/sample.zip zipfile

实测回显:

Analyzing demo/sample.zip
Detected format zip
Created org.apache.commons.compress.archivers.zip.ZipFile@6996db8
a.txt
Analyzing demo/sample.zip
Detected format zipfile
Exception in thread "main" org.apache.commons.compress.archivers.ArchiveException: Archiver: zipfile not found.
        at org.apache.commons.compress.archivers.ArchiveStreamFactory.createArchiveInputStream(ArchiveStreamFactory.java:477)
        at org.apache.commons.compress.archivers.ArchiveStreamFactory.createArchiveInputStream(ArchiveStreamFactory.java:418)

最后看 Lister 的边界:它只识别归档签名,tar.gz 这类外层压缩文件不处理:

cd /opt/compress-lab/demo/data && tar -czf ../sample.tar.gz a.txt && cd /opt/compress-lab
java -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" org.apache.commons.compress.archivers.Lister demo/sample.tar.gz

实测回显:

Analyzing demo/sample.tar.gz
Exception in thread "main" org.apache.commons.compress.archivers.ArchiveException: No Archiver found for the stream signature
        at org.apache.commons.compress.archivers.ArchiveStreamFactory.detect(ArchiveStreamFactory.java:289)
        at org.apache.commons.compress.archivers.Lister.detectFormat(Lister.java:62)
        at org.apache.commons.compress.archivers.Lister.list(Lister.java:122)
        at org.apache.commons.compress.archivers.Lister.go(Lister.java:114)
        at org.apache.commons.compress.archivers.Lister.main(Lister.java:84)

结论:Lister 只能「列纯归档」,不能解压、不能压缩、不能穿透 gzip/bzip2 外层。它更像 API 的自检工具,替代不了 tar/zip 命令。

2.1 jshell 交互实操

jshell 适合把库当「命令行」来探索。把实验语句存成脚本(jshell 脚本模式不会自动回显表达式值,必须显式 System.out.println):

cat > /opt/compress-lab/hello-compress.jsh <<'EOF'
import org.apache.commons.compress.compressors.*;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipParameters;
import java.io.*;

byte[] data = "jshell + commons-compress 命令行实验".getBytes("UTF-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
GzipParameters params = new GzipParameters();
params.setFileName("note.txt");
params.setCompressionLevel(java.util.zip.Deflater.BEST_COMPRESSION);
try (GzipCompressorOutputStream gz = new GzipCompressorOutputStream(bos, params)) { gz.write(data); }
System.out.println("gzip 输出大小: " + bos.size() + " 字节");

byte[] raw = bos.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(raw);
System.out.println("探测格式: " + CompressorStreamFactory.detect(in));

CompressorInputStream cis = new CompressorStreamFactory().createCompressorInputStream(new ByteArrayInputStream(raw));
byte[] restored = cis.readAllBytes();
cis.close();
System.out.println("解压还原: " + new String(restored, "UTF-8"));
/exit
EOF

该命令无输出。带类路径启动 jshell 执行脚本:

cd /opt/compress-lab
jshell --class-path "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" hello-compress.jsh

实测回显:

gzip 输出大小: 70 字节
探测格式: gz
解压还原: jshell + commons-compress 命令行实验

三个实测确认的 jshell 使用要点:导入子包要写全(compressors.* 不含 compressors.gzip.*,实测漏写会报 cannot find symbol: class GzipCompressorOutputStream);脚本文件模式不回显表达式值(实测 1+1 语句在脚本中无任何输出),交互式会话才会;/exit 结束会话。

2.2 javac + java -cp 手动运行示例程序

没有构建工具时,javac/java -cp 是运行库代码的标准方式。写一个最小验证程序 /opt/compress-lab/HelloCompress.java(去掉 package 行后保存):

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

public class HelloCompress {
    public static void main(String[] args) throws IOException {
        byte[] data = "hello commons compress".getBytes(StandardCharsets.UTF_8);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(bos)) {
            ZipArchiveEntry entry = new ZipArchiveEntry("hello.txt");
            entry.setSize(data.length);
            zos.putArchiveEntry(entry);
            zos.write(data);
            zos.closeArchiveEntry();
        }
        File tmp = File.createTempFile("hello", ".zip");
        Files.write(tmp.toPath(), bos.toByteArray());
        try (ZipFile zf = ZipFile.builder().setFile(tmp).setCharset("UTF-8").get();
             BufferedReader reader = new BufferedReader(
                     new InputStreamReader(zf.getInputStream(zf.getEntry("hello.txt")), StandardCharsets.UTF_8))) {
            System.out.println("zip 读回内容: " + reader.readLine());
        } finally {
            tmp.delete();
        }
        Package p = ZipArchiveEntry.class.getPackage();
        System.out.println("commons-compress 版本: " + p.getImplementationVersion());
        System.out.println("验证通过");
    }
}

编译并运行:

cd /opt/compress-lab
javac -encoding UTF-8 -cp "commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" HelloCompress.java
java -Dfile.encoding=UTF-8 -cp ".:commons-compress-1.28.0/commons-compress-1.28.0.jar:lib/*" HelloCompress

实测回显:

zip 读回内容: hello commons compress
commons-compress 版本: 1.28.0
验证通过

类路径写法要点:Linux 分隔符是冒号;. 让 JVM 找到当前目录的 HelloCompress.class;lib/* 由 JVM 展开,不要让 shell 先展开。

三、与系统命令对照

Commons Compress 能在命令行做的事有限,本节把它与 tar/gzip/xz/bzip2 系统命令逐项对照,帮读者判断什么时候该用谁。功能对照:

能力系统命令Commons Compress 命令行形态
列出 tar 内容tar -tf x.tarLister(-cp 启动,仅纯归档)
列出 zip 内容unzip -l x.zipLister
解包 tartar -xf x.tar无(需写 Java 程序)
压缩单文件gzip x.txt无(需写 Java 程序)
解压 tar.gztar -xzf x.tar.gz无(Lister 不识别压缩层)
创建归档tar -cf / zip无(需写 Java 程序)
自动探测格式无Lister 自动识别 + 库内 detect API
脚本化复杂流程shell 管道jshell 脚本 / Java 程序

性能对照:用一段 10,485,760 字节的重复文本实测四个系统命令(Ubuntu 22.04,time 实测):

yes "apache commons compress benchmark line 00001234 abcdefgh" | head -c 10M > bench.txt
time gzip -k -6 bench.txt
ls -l bench.txt.gz

实测回显:

real    0m0.071s
user    0m0.069s
sys     0m0.004s
-rw-r--r-- 1 root root 30594 Oct 13 08:23 bench.txt.gz

同样流程换 xz 与 bzip2(每次先删除上一次输出文件,-k 不会覆盖同名输出):

rm -f bench.txt.gz
time xz -k -6 bench.txt
ls -l bench.txt.xz
rm -f bench.txt.xz
time bzip2 -k -9 bench.txt
ls -l bench.txt.bz2
rm -f bench.txt bench.txt.bz2

实测回显:

real    0m0.253s
user    0m0.201s
sys     0m0.055s
-rw-r--r-- 1 root root 1712 Oct 13 08:23 bench.txt.xz
real    0m3.140s
user    0m3.132s
sys     0m0.010s
-rw-r--r-- 1 root root 2669 Oct 13 08:23 bench.txt.bz2

汇总成对照表(同一 10MB 输入,Ubuntu 22.04 实测):

命令耗时输出大小说明
gzip -60.071s30,594 B最快,兼容性最好
xz -60.253s1,712 B压缩比最高档
bzip2 -93.140s2,669 B比xz慢一个数量级,压缩比不如xz

库内同一份数据的对比(CompStreamDemo,1,348,890 字节输入实测):

gzip 级别1: 55677 字节
gzip 级别9: 52677 字节
bzip2     : 22000 字节
xz        : 26692 字节
lzma      : 26615 字节
deflate(9): 52620 字节

两侧结论一致:deflate 系(gzip/zip)最快但压缩比最低,xz/lzma 压缩比最高,bzip2 速度与压缩比都居中偏慢。选型建议:shell 交互与运维脚本用系统命令(零 Java 环境、有管道与 -k 等成熟参数);Java 程序内部处理归档/压缩用 Commons Compress(跨平台、可嵌入业务逻辑、支持 22 种格式);两者不要混用在同一条流水线里做半成品转换。

四、常见问题

Q1:java -jar commons-compress-1.28.0.jar 报 NoClassDefFoundError? 发行包 jar 虽有 Main-Class: org.apache.commons.compress.archivers.Lister,但 1.28.0 必需依赖 commons-lang3/commons-io/commons-codec,java -jar 会忽略 -cp。改用 java -cp "主jar:lib/*" org.apache.commons.compress.archivers.Lister 文件,见第二章实测。

Q2:Lister 能解压 tar.gz 吗? 不能。实测对 sample.tar.gz 报 ArchiveException: No Archiver found for the stream signature(Lister 只探测归档签名,不处理压缩层)。命令行解 tar.gz 用 tar -xzf,Java 程序内用两层流(压缩流套归档流)。

Q3:jshell 脚本跑完没有输出? 两个实测原因:脚本文件模式不回显表达式值,必须 System.out.println;导入子包要写全限定名,import org.apache.commons.compress.compressors.* 不包含 compressors.gzip 下的类。

Q4:终端里程序输出的中文变问号? locale 问题。export LANG=C.UTF-8,或给 java/jshell 加 -Dfile.encoding=UTF-8。若终端是 GBK 编码的远程工具,反过来设 -Dfile.encoding=GBK 或改终端为 UTF-8。

五、参考链接

  1. Apache Commons Compress 官方主页:https://commons.apache.org/proper/commons-compress/
  2. 1.28.0 javadoc(Lister 及工厂 API):https://commons.apache.org/proper/commons-compress/javadocs/api-release/index.html
  3. 1.28.0 发行包下载:https://downloads.apache.org/commons/compress/binaries/commons-compress-1.28.0-bin.tar.gz
  4. Maven 中央仓库 1.28.0:https://repo1.maven.org/maven2/org/apache/commons/commons-compress/1.28.0/
  5. GNU tar 手册:https://www.gnu.org/software/tar/manual/
  6. gzip 手册:https://www.gnu.org/software/gzip/manual/
  7. XZ Utils:https://tukaani.org/xz/
  8. jshell 用户指南:https://docs.oracle.com/en/java/javase/17/jshell/