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 Process(...).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
?
The various overloads of
Runtime.getRuntime().exec(...)
take either an array of strings or a single string. The single-string overloads ofexec()
will tokenise the string into an array of arguments, before passing the string array onto one of theexec()
overloads that takes a string array. TheProcessBuilder
constructors, on the other hand, only take a varargs array of strings or aList
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,
will run a
DoStuff.exe
program with the two given arguments. In this case, the command-line gets tokenised and put back together. However,will fail, unless there happens to be a program whose name is
DoStuff.exe -arg1 -arg2
inC:\
. This is because there's no tokenisation: the command to run is assumed to have already been tokenised. Instead, you should useor alternatively
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.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()
andRuntime.exec()
because implementation ofRuntime.exec()
is:So code:
should be the same as:
Thanks dave_thompson_085 for comment
Look at how
Runtime.getRuntime().exec()
passes the String command to theProcessBuilder
. It uses a tokenizer and explodes the command into individual tokens, then invokesexec(String[] cmdarray, ......)
which constructs aProcessBuilder
.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 aString...
vararg, so passing the whole command as a single String has the same effect as invoking that command in quotes in a terminal: