使用ProcessBuilder时Java不启动GZIP命令

时间:2018-10-10 18:59:21

标签: java processbuilder

我肯定在尝试此操作的盒子上安装了GZIP。但是,当我在Java中运行此文件时,看不到正在创建的压缩文件。我正在处理的文件是非常大的文件,因此我不希望将其读入内存。以下是我为此编写的代码。我的直觉是它与重定向有关。

try {
    ProcessBuilder builder = new ProcessBuilder("gzip", "-9", "<", filename, ">", zippedFilename);
    builder.start();
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

1 个答案:

答案 0 :(得分:2)

首先,值得注意的是,您可以通过避免外部过程并使用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);
}

这具有完全多平台的额外优势。在Windows上不需要/ usr / bin / gzip,也不需要Unix工具。它不会实现-9选项,但是我会检查该选项实际上能获得多少额外的压缩,并权衡使用一个不那么便携的程序是否值得。

对于其他命令(或者如果-9非常重要),由于C程序会出于相同的原因,ProcessBuilder命令无法使用<>进行输入和输出的重定向。无法通过以下呼叫完成重定向:

/* 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进程不知道其输入来自何处或其输出在何处。它只是从其自己的标准输入读取并写入其标准输出。

直接调用程序时,将绕过外壳程序,因此对<>没有特殊处理。这意味着您当前的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程序的标准错误中。否则,您将无法指示程序为何失败。 (如果我们没有重定向它们,则对标准输入和标准输出也一样。)