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

发布于 2024-11-26 12:30:34 字数 862 浏览 2 评论 0原文

我正在尝试从 java 代码执行外部命令,但我注意到 Runtime.getRuntime().exec(...)new ProcessBuilder(.. .).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 的问题?

I'm trying to execute an external command from java code, but there's a difference I've noticed between Runtime.getRuntime().exec(...) and new ProcessBuilder(...).start().

When using Runtime:

Process p = Runtime.getRuntime().exec(installation_path + 
                                       uninstall_path + 
                                       uninstall_command + 
                                       uninstall_arguments);
p.waitFor();

the exitValue is 0 and the command is terminated ok.

However, with ProcessBuilder:

Process p = (new ProcessBuilder(installation_path +    
                                 uninstall_path +
                                 uninstall_command,
                                 uninstall_arguments)).start();
p.waitFor();

the exit value is 1001 and the command terminates in the middle, although waitFor returns.

What should I do to fix the problem with ProcessBuilder?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

完美的未来在梦里 2024-12-03 12:30:34

Runtime.getRuntime().exec(...) 的各种重载采用字符串数组或单个字符串。 exec() 的单字符串重载会将字符串标记为参数数组,然后将字符串数组传递给采用字符串的 exec() 重载之一大批。另一方面,ProcessBuilder 构造函数仅采用字符串的 varargs 数组或字符串的 List,其中数组或列表中的每个字符串都被假定为单独的字符串。争论。无论哪种方式,获得的参数都会被连接成一个字符串,传递给操作系统来执行。

因此,例如,在 Windows 上,

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");

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

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");

除非 C:\ 中恰好有一个名称为 DoStuff.exe -arg1 -arg2 的程序,否则将会失败。这是因为没有标记化:假设要运行的命令已经被标记化。相反,您应该使用

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);

The various overloads of Runtime.getRuntime().exec(...) take either an array of strings or a single string. The single-string overloads of exec() will tokenise the string into an array of arguments, before passing the string array onto one of the exec() overloads that takes a string array. The ProcessBuilder constructors, on the other hand, only take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument. Either way, the arguments obtained are then joined up into a string that is passed to the OS to execute.

So, for example, on Windows,

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");

will run a DoStuff.exe program with the two given arguments. In this case, the command-line gets tokenised and put back together. However,

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");

will fail, unless there happens to be a program whose name is DoStuff.exe -arg1 -arg2 in C:\. This is because there's no tokenisation: the command to run is assumed to have already been tokenised. Instead, you should use

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");

or alternatively

List<String> params = java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
深海夜未眠 2024-12-03 12:30:34

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的评论

There are no difference between ProcessBuilder.start() and Runtime.exec() because implementation of Runtime.exec() is:

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();
}

So code:

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();

should be the same as:

Runtime.exec(command)

Thanks dave_thompson_085 for comment

但可醉心 2024-12-03 12:30:34

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

如果您使用字符串数组而不是单个字符串来构造 ProcessBuilder,您将得到相同的结果。

ProcessBuilder 构造函数采用 String... 可变参数,因此将整个命令作为单个字符串传递与在终端中用引号调用该命令具有相同的效果:

shell$ "command with args"

Look at how Runtime.getRuntime().exec() passes the String command to the ProcessBuilder. It uses a tokenizer and explodes the command into individual tokens, then invokes exec(String[] cmdarray, ......) which constructs a ProcessBuilder.

If you construct the ProcessBuilder with an array of strings instead of a single one, you'll get to the same result.

The ProcessBuilder constructor takes a String... vararg, so passing the whole command as a single String has the same effect as invoking that command in quotes in a terminal:

shell$ "command with args"
↙厌世 2024-12-03 12:30:34

是的,有区别。

  • Runtime.exec(String) 方法 接受单个命令字符串,并将其拆分为命令和参数序列。

  • ProcessBuilder 构造函数 采用 (varargs) 字符串数组。第一个字符串是命令名称,其余字符串是参数。 (有一个替代构造函数接受字符串列表,但没有一个接受由命令和参数组成的单个字符串。)

因此,您告诉 ProcessBuilder 要做的是执行名称包含空格和其他垃圾的“命令”在其中。当然,操作系统找不到该名称的命令,命令执行失败。

Yes there is a difference.

  • The Runtime.exec(String) method takes a single command string that it splits into a command and a sequence of arguments.

  • The ProcessBuilder constructor takes a (varargs) array of strings. The first string is the command name and the rest of them are the arguments. (There is an alternative constructor that takes a list of strings, but none that takes a single string consisting of the command and arguments.)

So what you are telling ProcessBuilder to do is to execute a "command" whose name has spaces and other junk in it. Of course, the operating system can't find a command with that name, and the command execution fails.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文