The following examples were prepared by Java engineering for the benefit of Java developers who may have faced issues with Runtime.exec
on the Windows platform.
Background
In JDK 7u21, the decoding of command strings specified to Runtime.exec(String), Runtime.exec(String,String[])
and Runtime.exec(String,String[],File)
methods, has been made more strict. See JDK 7u21 Release Notes for more information.
This caused several issues for applications. The following section describes some of the problems faced by developers and their solutions.
Note: In JDK 7u25, the system property jdk.lang.Process.allowAmbigousCommands
can be used to relax the checking process and helps as a workaround for some applications that cannot be changed. The workaround is only effective for applications that are run without a SecurityManager
.
See JDK 7u25 Release Notes for more information.
Note: To understand the details of the Windows API CreateProcess call, see:http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx
There are two forms of Runtime.exec
calls:
- with the command as string: "Runtime.exec(String command[, ...])"
- with the command as string array: "Runtime.exec(String[] cmdarray [, ...] )"
The issues described in this section relate to the first form of call. With the first call form, developers expect the command to be passed "as is" to Windows where the command needs be split into its executable name and arguments parts first. But, in accordance with Java API, the command argument is split into executable name and arguments by spaces.
Problem 1: "The file path for the command includes spaces"
In the call:
Runtime.getRuntime().exec("c:\\Program Files\\do.exe")
the argument is split by spaces to an array of strings as:
c:\\Program, Files\\do.exe
The first element of parsed array is interpreted as the executable name, verified by SecurityManager
(if present) and surrounded by quotations to avoid ambiguity in executable path.
This results in the wrong command:
"c:\\Program""Files\\do.exe"
which will fail.
Solution:
Use the ProcessBuilder
class, or the Runtime.exec(String[] cmdarray [, ...] )
call, or quote the executable path.
Where it is not possible to change the application code and where a SecurityManager
is not used, the Java property jdk.lang.Process.allowAmbigousCommands
could be used by setting its value to "true" from the command line:
-Djdk.lang.Process.allowAmbigousCommands=true
This will relax the checking process to allow ambiguous input.
Examples:
new ProcessBuilder("c:\\Program Files\\do.exe").start()
Runtime.getRuntime().exec(new String[]{"c:\\Program Files\\do.exe"})
Runtime.getRuntime().exec("\"c:\\Program Files\\do.exe\"")
Problem 2: "Shell command/.bat/.cmd IO redirection"
The following implicit cmd.exe
calls:
Runtime.getRuntime().exec("dir > temp.txt")
new ProcessBuilder("foo.bat", ">", "temp.txt").start()
Runtime.getRuntime().exec(new String[]{"foo.cmd", ">", "temp.txt"})
lead to the wrong command:
"XXXX"">" temp.txt
Solution:
To specify the command correctly, use the following options:
Runtime.getRuntime().exec("cmd /C \"dir > temp.txt\"")
new ProcessBuilder("cmd", "/C", "foo.bat > temp.txt").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "foo.cmd > temp.txt"})
or
Process p = new ProcessBuilder("cmd", "/C""XXX").redirectOutput(new File("temp.txt")).start();
Problem 3: "Group execution of shell command and/or .bat/.cmd files"
Due to enforced verification procedure, arguments in the following calls create the wrong commands.:
Runtime.getRuntime().exec("first.bat && second.bat")
new ProcessBuilder("dir", "&&", "second.bat").start()
Runtime.getRuntime().exec(new String[]{"dir", "|", "more"})
Solution:
To specify the command correctly, use the following options:
Runtime.exec("cmd /C \"first.bat && second.bat\"")
new ProcessBuilder("cmd", "/C", "dir && second.bat").start()
Runtime.exec(new String[]{"cmd", "/C", "dir | more"})
The same scenario also works for the "&"
, "||"
, "^"
operators of the cmd.exe
shell.
Problem 4: ".bat/.cmd with special DOS chars in quoted params”
Due to enforced verification, arguments in the following calls will cause exceptions to be thrown.:
Runtime.getRuntime().exec("log.bat \">error<\"")
new ProcessBuilder("log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"log.bat", ">error<"})
Solution:
To specify the command correctly, use the following options:
Runtime.getRuntime().exec("cmd /C log.bat \">error<\"")
new ProcessBuilder("cmd", "/C", "log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "log.bat", ">error<"})
Examples:
Complicated redirection for shell construction:
cmd /c dir /b C:\ >"my lovely spaces.txt"
becomes
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "dir \b >\"my lovely spaces.txt\"" });
The Golden Rule:
In most cases, cmd.exe
has two arguments: "/C" and the command for interpretation.