我确实在尝试此操作的机器上安装了 GZIP
。但是当我在 Java 中运行它时,我没有看到正在创建的压缩文件。我正在处理的文件是一个非常大的文件,我宁愿不将其读入内存。以下是我为此目的编写的代码。我的直觉是它与重定向有关。
try {
ProcessBuilder builder = new ProcessBuilder("gzip", "-9", "<", filename, ">", zippedFilename);
builder.start();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
最佳答案
首先,值得注意的是,您可以通过避免外部进程并使用 Java 进行压缩来使这一过程变得更容易:
Path input = Paths.get(filename);
Path zipped = Paths.get(zippedFilename);
try (OutputStream out = new GZIPOutputStream(
new BufferedOutputStream(
Files.newOutputStream(zipped)))) {
Files.copy(input, out);
}
这还有一个额外的优势,那就是完全跨平台。不需要/usr/bin/gzip,不需要 Windows 上的 Unix 工具。它不会实现-9
选项,但我会检查该选项实际上获得了多少额外压缩,并权衡是否值得拥有一个不太可移植的程序。
对于其他命令(或者如果 -9
非常重要),ProcessBuilder 命令无法使用 <
进行输入和输出重定向。和>
,出于同样的原因,C 程序无法通过如下调用完成重定向:
/* Does not work. */
execl("/usr/bin/gzip", "gzip", "-9", "<", filename, ">", zippedFilename, (char *)NULL);
当您在 shell 中运行命令(如 bash)时,shell 会拦截 <
和>
,从命令中剥离它们及其后续参数,并在没有它们的情况下调用实际程序。因此,输入:
gzip -9 < filename > filename.gz
实际上会导致 shell 运行 gzip
只有一个参数:-9
。然后 shell 从 filename
读取并将其传递给 gzip 程序进程的标准输入描述符。同样,shell 从同一 gzip 程序调用捕获标准输出,并将其写入 filename.gz
.
当这种情况发生时,gzip 进程不知道它的输入来自哪里,或者它的输出要去哪里。它只是从自己的标准输入中读取并写入其标准输出。
当您直接调用程序时,您将绕过 shell,因此无需对 <
进行特殊处理。和>
。这意味着您当前的 ProcessBuilder 命令相当于以下 Unix 命令:
gzip -9 '<' filename '>' filename.gz
这意味着您正在使用一个选项和四个文件参数调用 gzip,这会导致 gzip 首先查找名称为一个字符长的文件,即字面上名为 <
的文件。 ,然后将其压缩版本写入 <.gz
。然后,它将对名为 filename
的文件执行相同的操作。 ,然后是一个名为 >
的文件,然后是一个名为 filename.gz
的文件.
因此,如您所见,Unix 命令对重定向一无所知。 <
和>
字符不能直接传递给它们。
但是,您可以使用 ProcessBuilder 模拟重定向:
ProcessBuilder builder = new ProcessBuilder("gzip", "-9");
builder.inheritIO();
builder.redirectInput(new File(filename));
builder.redirectOutput(new File(zippedFilename));
Process process = builder.start();
调用inheritIO()将导致外部进程的标准错误(即任何错误消息)出现在Java程序的标准错误上。如果没有这个,你就无法知道程序失败的原因。 (如果我们没有重定向它们,它会对标准输入和标准输出执行相同的操作。)
关于使用 ProcessBuilder 时 Java 不启动 GZIP 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52747069/