我正在尝试从java代码执行外部命令,但我注意到Runtime.getRuntime().exec(...)
和new Process(...).start()
之间存在差异。
使用Runtime
时:
Process p = Runtime.getRuntime().exec(installation_path +
uninstall_path +
uninstall_command +
uninstall_arguments);
p.waitFor();
exitValue为0,命令终止确定。
然而,ProcessBuilder
:
Process p = (new ProcessBuilder(installation_path +
uninstall_path +
uninstall_command,
uninstall_arguments)).start();
p.waitFor();
退出值为1001,命令在中间终止,但waitFor
返回。
我应该怎样做才能解决ProcessBuilder
的问题?
答案 0 :(得分:90)
Runtime.getRuntime().exec(...)
的各种重载采用字符串数组或单个字符串。在将字符串数组传递到带有字符串数组的exec()
重载之一之前,exec()
的单字符串重载会将字符串标记为参数数组。另一方面,ProcessBuilder
构造函数只接受字符串的varargs数组或字符串的List
,其中数组或列表中的每个字符串都被假定为单个参数。无论哪种方式,获得的参数然后连接成一个字符串,传递给OS执行。
因此,例如,在Windows上,
Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");
将运行带有两个给定参数的DoStuff.exe
程序。在这种情况下,命令行会被标记化并重新组合在一起。然而,
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");
将失败,除非恰好在DoStuff.exe -arg1 -arg2
中有一个名为C:\
的程序。这是因为没有令牌化:假设运行的命令已被标记化。相反,你应该使用
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");
或者
List<String> params = java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
答案 1 :(得分:17)
查看Runtime.getRuntime().exec()
如何将String命令传递给ProcessBuilder
。它使用一个标记化器并将命令分解为单个标记,然后调用构造exec(String[] cmdarray, ......)
的{{1}}。
如果使用字符串数组而不是单个字符串构造ProcessBuilder
,则会得到相同的结果。
ProcessBuilder
构造函数采用ProcessBuilder
vararg,因此将整个命令作为单个String传递与在终端中的引号中调用该命令具有相同的效果:
String...
答案 2 :(得分:13)
是的,有区别。
Runtime.exec(String)
method接受一个命令字符串,它分成命令和一系列参数。
ProcessBuilder
constructor采用(varargs)字符串数组。第一个字符串是命令名称,其余的是参数。
所以你告诉ProcessBuilder要做的是执行一个名称中包含空格和其他垃圾的“命令”。当然,操作系统找不到具有该名称的命令,并且命令执行失败。
答案 3 :(得分:11)
ProcessBuilder.start()
和Runtime.exec()
之间没有区别,因为Runtime.exec()
的实施是:
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
public Process exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
所以代码:
List<String> list = new ArrayList<>();
new StringTokenizer(command)
.asIterator()
.forEachRemaining(str -> list.add((String) str));
new ProcessBuilder(String[])list.toArray())
.environment(envp)
.directory(dir)
.start();
应与:
相同Runtime.exec(command)
感谢 dave_thompson_085 发表评论