java - 在 java 中使用 ProcessBuilder 提取 7zip cmd
问题描述
我在使用带有命令行的 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
您是否知道是什么导致了这里的差异以及为什么它说“没有要处理的文件,一切正常”而不做任何事情?我已经尝试过首先使用 File 类创建一个文件夹,但这似乎不是问题,因为无论目标文件夹在提取之前是否存在,结果都是相同的。
我已经尝试了所有想到的东西,但我现在已经没有想法了。请与我分享您对此问题的任何建议。非常感谢。
解决方案
非常感谢您的帮助。
不要引用你的论点。引号是为了命令 shell 的好处。ProcessBuilder 不是命令外壳;它直接执行命令,因此任何引号都被视为参数本身(即文件名)的一部分。另外, pb.inheritIO(); 是比手动使用流程流更好的查看子流程输出的方法。
谢谢@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";
}
双击文件 7zip.chm 或启动 7-Zip 并打开帮助并阅读帮助页面命令行版本 - 第一行 7z [...] [...] 的语法。有明确的解释,首先必须指定命令 x,接下来应该是像 -o 这样的开关,最好的最后一个开关是 --,然后是存档文件名和最后一个参数,如要提取的文件/文件夹的名称。开关也可以在存档文件名之后指定,但不建议这样做,尽管帮助页面上的 -o 示例也以 -o 结尾。
谢谢@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
再次非常感谢您的帮助!
推荐阅读
- scala - Scala列表列动态地从列表中删除列
- java - 暂停和恢复安卓游戏->
- python - 在 Python 中将目录的所有内容移动到另一个目录
- php - PHP 根据另一个数组的值创建新数组
- excel - 使用 Powershell 将多个工作簿合并为一个工作簿工作表
- r - 在 read.csv 中提及 colClasses 如何影响我在 R 中的程序
- typescript - TypeScript addEventListener
- pacemaker - Ping 约束不移动 DRBD 主
- powershell - 在模块“SmbShare”中找到“New-SmbMapping”命令,但无法加载该模块
- python - scapy.srp 不给所有客户