在 java 中使用 ProcessBuilder 提取 7zip cmd

7zip cmd extracting using ProcessBuilder in java

我在使用 Java 10 ProcessBuilder 和 7z.exe (18.05) 命令行将存档提取到所需类别时遇到问题。当我使用 Windows CMD 时,完全相同的命令按预期工作,但当我的 JavaFX 应用程序使用 ProcessBuilder 发出时不再起作用:

    public static void decompress7ZipEmbedded(File source, File destination) throws IOException, InterruptedException {
    ProcessBuilder pb = new ProcessBuilder(
            getSevenZipExecutablePath(),
            EXTRACT_WITH_FULL_PATHS_COMMAND,
            quotifyPath(source.getAbsolutePath()),
            OUTPUT_DIRECTORY_SWITCH + quotifyPath(destination.getAbsolutePath())
    );
    processWithSevenZipEmbedded(pb);
}

   private static void processWithSevenZipEmbedded(ProcessBuilder pb) throws IOException, InterruptedException {
    LOG.info("7-zip command issued: " + String.join(" ", pb.command()));
    Process p = pb.start();
    new Thread(new InputConsumer(p.getInputStream())).start();
    System.out.println("Exited with: " + p.waitFor());
}

   public static class InputConsumer implements Runnable {
        private InputStream is;

        InputConsumer(InputStream is) {
            this.is = is;
        }

        @Override
        public void run() {
            try {
                int value = -1;
                while ((value = is.read()) != -1) {
                    System.out.print((char) value);
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }
            LOG.debug("Output stream completed");
        }
    }

public static String getSevenZipExecutablePath() {
    return FileUtil.quotifyPath(getDirectory() + "7z" + "/" + "7z");
}

public static String quotifyPath(String path) {
    return '"' + path + '"';
}

public class Commands {

    public static final String EXTRACT_COMMAND = "e";
    public static final String EXTRACT_WITH_FULL_PATHS_COMMAND = "x";
    public static final String PACK_COMMAND = "a";
    public static final String DELETE_COMMAND = "d";
    public static final String BENCHMARK_COMMAND = "b";
    public static final String LIST_COMMAND = "l";

}

public class Switches {

    public static final String OUTPUT_DIRECTORY_SWITCH = "-o";
    public static final String RECURSIVE_SWITCH = "-r";
    public static final String ASSUME_YES = "y";
}

命令如下所示:

"C:/Users/blood/java_projects/AppRack/target/classes/7z/7z" x "D:\Pulpit\AppRack Sandbox\test\something\Something 2\Something2.7z" -o"D:\Pulpit\AppRack Sandbox\Something2"

ProcessBuilder 的输出:

7-Zip 18.05 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2018-04-30

Scanning the drive for archives:
1 file, 59177077 bytes (57 MiB)

Extracting archive: D:\Pulpit\AppRack Sandbox\test\Something\Something 2\Something2.7z
--
Path = D:\Pulpit\AppRack Sandbox\test\Something\Something 2\Something2.7z
Type = 7z
Physical Size = 5917Exited with: 0
7077
Headers Size = 373
Method = LZMA2:26 LZMA:20 BCJ2
Solid = +
Blocks = 2


No files to process
Everything is Ok

Files: 0
Size:       0
Compressed: 59177077

它什么也没做。不创建所需的文件夹,什么都没有。使用 CMD 它就像一个魅力(这里使用 相同的命令 从 Windows 10 CMD 登录):

7-Zip 18.05 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2018-04-30

Scanning the drive for archives:
1 file, 59177077 bytes (57 MiB)

Extracting archive: D:\Pulpit\AppRack Sandbox\test\Something\Something 2\Something2.7z
--
Path = D:\Pulpit\AppRack Sandbox\test\Something\Something 2\Something2.7z
Type = 7z
Physical Size = 59177077
Headers Size = 373
Method = LZMA2:26 LZMA:20 BCJ2
Solid = +
Blocks = 2

Everything is Ok

Folders: 1
Files: 5
Size:       64838062
Compressed: 59177077

您是否知道是什么导致了这里的差异,以及为什么它显示 "No files to process, everything is ok" 却什么都不做?我已经尝试先使用 File class 创建一个文件夹,但这似乎不是问题,因为无论目标文件夹在提取之前是否存在,结果都是相同的。

我已经尝试了我想到的所有方法,但我 运行 目前没有想法。请与我分享您对这个问题的任何建议。非常感谢。

非常感谢您的帮助。

Don’t quote your arguments. Quotes are for the command shell’s benefit. ProcessBuilder is not a command shell; it executes a command directly, so any quotes are seen as part of the argument itself (that is, the file name). Also, pb.inheritIO(); is a better way to see the output of the child process than manually consuming process streams.

谢谢@VGR 这似乎是问题所在 - 在我删除了上述命令中引用路径的方法后,它就像一个魅力一样工作并且毫无问题地提取存档!所以结论是我不应该在使用 Java ProcessBuilder 时在路径中使用引号。

我也用过 pb.inheritIO(),你是对的,用这种方式管理它更好更容易。

public static void decompress7ZipEmbedded(File source, File destination) throws IOException {
    ProcessBuilder pb = new ProcessBuilder().inheritIO().command(
            getSevenZipExecutablePath(),
            EXTRACT_WITH_FULL_PATHS_COMMAND,
            source.getAbsolutePath(),
            OUTPUT_DIRECTORY_SWITCH + destination.getAbsolutePath(),
            OVERWRITE_WITHOUT_PROMPT
    );
    processWithSevenZipEmbedded(pb);
}

private static void processWithSevenZipEmbedded(ProcessBuilder pb) throws IOException {
    LOG.info("7-zip command issued: " + String.join(" ", pb.command()));
    pb.start();
}

public class Commands {
    public static final String EXTRACT_WITH_FULL_PATHS_COMMAND = "x"; 
}

public class Switches {
    public static final String OUTPUT_DIRECTORY_SWITCH = "-o";
    public static final String OVERWRITE_WITHOUT_PROMPT = "-aoa";
}

Double click on file 7zip.chm or start 7-Zip and open the Help and read the help page Command Line Version - Syntax with first line 7z [...] [...]. There is clearly explained that first the command x must be specified, next should be the switches like -o with best last switch being --, then the archive file name and last further arguments like names of files/folders to extract. Switches can be also specified after archive file name, but that is not recommended although examples on help page for -o are also with -o at end.

谢谢@Mofi 的提示。我使用 -aoa 开关而不是 -y ,它终于开始按我想要的方式工作 - 在没有任何提示的情况下覆盖文件。我按照预期的方式保留了命令的其余部分,所以它最终看起来像这样:

C:/Users/blood/java_projects/AppRack/target/classes/7z/7z" x D:\Pulpit\AppRack Sandbox\test\Test\Test 2\Test.7z -oD:\Desktop\AppRack Sandbox\Test 2 -aoa

再次感谢您的帮助!