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
?
This question is related to
java
runtime.exec
processbuilder
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"
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.
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
Source: Stackoverflow.com