请教Runtime.getRuntime().exec(String[] cmdarry)参数的问题

请教Runtime.getRuntime().exec(String[] cmdarry)参数的问题

问题描述:

环境windows下,比如我要通过java调用一个程序,程序有一个参数:
程序路径:C:\my dir\myexe.exe
参数:[color=red]param="my parameter"[/color]
调用时:cmd[0] = "C:\my dir\myexe.exe"
cmd[1] = "param=\"my parameter\""
说明:其中整个红色部分是参数,参数中有一个空格,我使用Runtime.getRuntime().exec(String[] cmdarry)以后,发现传进去的参数个数为3,从my后面被截断了,而且引号也不见了,难道不是把cmd[1]整个当作参数吗?疑惑。

啊,原来如此。JDK里java.lang.ProcessImpl.start()的实现:
[quote="JDK 1.6.0_05"][code="java"] // System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[],
java.util.Map environment,
String dir,
boolean redirectErrorStream)
throws IOException
{
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
return new ProcessImpl(cmdarray, envblock, dir, redirectErrorStream);
}

private long handle = 0;
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;

private ProcessImpl(String cmd[],
        String envblock,
        String path,
        boolean redirectErrorStream)
throws IOException
{
// Win32 CreateProcess requires cmd[0] to be normalized
cmd[0] = new File(cmd[0]).getPath();

StringBuilder cmdbuf = new StringBuilder(80);
for (int i = 0; i < cmd.length; i++) {
        if (i > 0) {
            cmdbuf.append(' ');
        }
    String s = cmd[i];
    if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) {
        if (s.charAt(0) != '"') {
        cmdbuf.append('"');
        cmdbuf.append(s);
        if (s.endsWith("\\")) {
        cmdbuf.append("\\");
        }
        cmdbuf.append('"');
            } else if (s.endsWith("\"")) {
        /* The argument has already been quoted. */
        cmdbuf.append(s);
    } else {
        /* Unmatched quote for the argument. */
        throw new IllegalArgumentException();
    }
    } else {
        cmdbuf.append(s);
    }
}
String cmdstr = cmdbuf.toString();

stdin_fd  = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();

handle = create(cmdstr, envblock, path, redirectErrorStream,
        stdin_fd, stdout_fd, stderr_fd);

java.security.AccessController.doPrivileged(
    new java.security.PrivilegedAction() {
    public Object run() {
    stdin_stream =
        new BufferedOutputStream(new FileOutputStream(stdin_fd));
    stdout_stream =
        new BufferedInputStream(new FileInputStream(stdout_fd));
    stderr_stream =
        new FileInputStream(stderr_fd);
    return null;
    }
});
}[/code][/quote]

可以看到Java标准库自作主张的给带有空白字符的参数的头尾加了引号。也就是说原本给进去的参数是:
[quote]param="my parameter"[/quote]
的话,被处理了之后就变成:
[quote]"param="my parameter""[/quote]
于是Win32的CreateProcess看到的就是:
[quote]param=my parameter[/quote]
(引号会由操作系统决定如何吃掉)

[code="java"]import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Test {
public static void main(String[] args) throws Exception {
if (0 < args.length) {
for (String arg : args) {
System.out.println(arg);
}
} else {
Runtime rt = Runtime.getRuntime();
Process test = rt.exec(new String[] { "java.exe", "Test", "1stArg=alpha", "2ndArg=beta charlie", "3rdArg=\"delta echo\"" });

        InputStream in = test.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = null;
        while (null != (line = reader.readLine())) {
            System.out.println(line);
        }
    }
}

}[/code]
输出是:
[quote]1stArg=alpha
2ndArg=beta charlie
3rdArg=delta
echo[/quote]
你看,不加引号就没事……

引号的处理应该是依赖于操作系统的行为来的。不过这里具体为什么被截断了倒是挺奇怪的……