ProcessBuilder和Runtime.exec()之间的区别

gal*_*gal 93 java runtime.exec processbuilder

我正在尝试从java代码执行一个外部命令,但是我注意到Runtime.getRuntime().exec(...)和之间存在差异new ProcessBuilder(...).start().

使用时Runtime:

Process p = Runtime.getRuntime().exec(installation_path + 
                                       uninstall_path + 
                                       uninstall_command + 
                                       uninstall_arguments);
p.waitFor();
Run Code Online (Sandbox Code Playgroud)

exitValue为0,命令终止ok.

但是,有ProcessBuilder:

Process p = (new ProcessBuilder(installation_path +    
                                 uninstall_path +
                                 uninstall_command,
                                 uninstall_arguments)).start();
p.waitFor();
Run Code Online (Sandbox Code Playgroud)

退出值为1001,命令终止于中间,但waitFor返回.

我该怎么做才能解决问题ProcessBuilder

Luk*_*ard 95

采用Runtime.getRuntime().exec(...)字符串数组或单个字符串的各种重载.exec()在将字符串数组传递给exec()带有字符串数组的重载之一之前,单字符串重载会将字符串标记为参数数组.的ProcessBuilder构造,在另一方面,只需要串的可变参数阵列或List字符串,其中假定该阵列或列表中的每个字符串为一个单独的参数的.无论哪种方式,获得的参数然后被连接成一个字符串,该字符串被传递给OS来执行.

所以,例如,在Windows上,

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");
Run Code Online (Sandbox Code Playgroud)

DoStuff.exe使用两个给定的参数运行程序.在这种情况下,命令行会被标记化并重新组合在一起.然而,

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");
Run Code Online (Sandbox Code Playgroud)

将失败,除非恰好是他的名字是一个程序DoStuff.exe -arg1 -arg2C:\.这是因为没有令牌化:假设运行的命令已被标记化.相反,你应该使用

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");
Run Code Online (Sandbox Code Playgroud)

或者

List<String> params = java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
Run Code Online (Sandbox Code Playgroud)

  • 我无法相信这个字符串连接有任何意义:"installation_path + uninstall_path + uninstall_command". (7认同)
  • Runtime.getRuntime().exec(...)执行*NOT*调用shell,除非命令明确指定.关于最近的"Shellshock"bug问题,这是一件好事.这个答案有误导性,因为它表明将运行cmd.exe或等效的(即unix上的/ bin/bash),这似乎不是这种情况.而是在Java环境中完成标记化. (7认同)

Cos*_*atu 17

看看如何Runtime.getRuntime().exec()将String命令传递给ProcessBuilder.它使用一个标记化器并将命令分解为单个标记,然后调用exec(String[] cmdarray, ......)构造一个标记ProcessBuilder.

如果ProcessBuilder使用字符串数组而不是单个字符串构造,则会得到相同的结果.

所述ProcessBuilder构造函数采用一个String...可变参数,以便使整个命令作为一个单一的字符串与调用相同的效果,即在在终端引号命令:

shell$ "command with args"
Run Code Online (Sandbox Code Playgroud)


Ste*_*n C 13

是,有一点不同.

所以你告诉ProcessBuilder要做的是执行一个名称中包含空格和其他垃圾的"命令".当然,操作系统找不到具有该名称的命令,并且命令执行失败.

  • 你错了。阅读源代码!`Runtime.exec(cmd)` 实际上是 `Runtime.exec(cmd.split("\\s+"))` 的快捷方式。`ProcessBuilder` 类没有直接等效于 `Runtime.exec(cmd)` 的构造函数。这就是我在回答中要表达的观点。 (2认同)

Eug*_*kin 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();
}
Run Code Online (Sandbox Code Playgroud)

所以代码:

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();
Run Code Online (Sandbox Code Playgroud)

应该是这样的:

Runtime.exec(command)
Run Code Online (Sandbox Code Playgroud)

感谢dave_thompson_085的评论

  • 但是Q不会调用该方法。它(间接)调用`public Process exec(String command,String [] envp,File dir)`-`String` NOT`String []`-调用`StringTokenizer`并将令牌放入一个数组中,然后该数组(间接)传递给了“ ProcessBuilder”,这与7年前的三个答案所指出的完全不同。 (2认同)