通过 Runtime.exec() 执行管道 ps unix 命令

Issue executing a piped ps unix command through Runtime.exec()

问题:

当通过 Runtime.exec(...) 执行以下命令时,它在寻找匹配的引号字符时失败并出现意外的 EOF。

一个奇怪的地方是错误消息有一个严重的字符后跟两个单引号。

但是,当我通过putty执行在日志中打印出来的命令时,它工作正常。

命令:

bin/sh -c 'ps -eo uname,pid,ppid,nlwp,pcpu,pmem,psr,start_time,tty,time,args | fgrep IAAPC | fgrep /f1/f2/a00-a/f3/server/server_1/env_1/javadriver | fgrep -v fgrep'

产生的错误:

-eo: -c: line 0: unexpected EOF while looking for matching `''
-eo: -c: line 1: syntax error: unexpected end of file

Java代码(Java1.6 ...不要判断):

    String driverHome = trimToEmpty(System.getProperty("batchdriver.home"));
    String cmd = "/bin/sh -c 'ps -eo uname,pid,ppid,nlwp,pcpu,pmem,psr,start_time,tty,time,args | fgrep "+jobName+" | fgrep "+driverHome+" | fgrep -v fgrep'";
    String out = null, err = null;
    Process proc = null;
    try {
        proc = Runtime.getRuntime().exec(cmd);
        
        out = fullyRead(proc.getInputStream());
        err = fullyRead(proc.getErrorStream());
        
        int exitVal = proc.waitFor();
        
        if(logger.isDebugEnabled()) {
            logger.debug("Process Information: "+out);
        }
        if (isNotEmpty(err)) {
            logger.error(failedCommandMessage(cmd, out, err));
            this.processId = null;
            this.processDesc = PROCESS_NOT_FOUND;
            return;
        }
        
        String[] processes = StringUtils.split(out, "\r?\n");
        if (processes == null || processes.length == 0) {
            this.processDesc = PROCESS_NOT_FOUND;
        }
        else if (processes.length == 1) {
            String[] processInfo = processes[0].split("\s+");
            this.processId = processInfo[1];
            if (!isNumeric(this.processId)) {
                this.processId = null;
            }
            this.processDesc = out;
        }
        else {
            this.processDesc = out;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Call to the OS completed with exit value: " + exitVal);
        }
    } catch (Exception e) {
        try {out = fullyRead(proc.getInputStream());} catch (Exception e1) {}
        try {err = fullyRead(proc.getErrorStream());} catch (Exception e1) {}
        this.processId = null;
        this.processDesc = PROCESS_NOT_FOUND;
        logger.error(failedCommandMessage(cmd, out, err), e);
    }

相关但不完全欺骗:

The Runtime.exec methods that take a String break it into tokens at whitespace only 所以这实际上是 运行 程序 /bin/sh (a shell) 具有以下参数:

 -c
 'ps
 -eo 
 uname,pid,ppid,nlwp,pcpu,pmem,psr,start_time,tty,time,args 
 |
 fgrep
 ...

shell 像这样解释这些参数:

 -c 'ps -- the script to run consists of the apostrophe character, p, s (and nothing more)
 -eo -- the name of the command being run is -eo 
 uname,pid,.... -- the first argument to the script is this
 | -- the second argument to the script is this
 fgrep -- the third argument to the script is this
 ... 
 -- but the script ignores the arguments and doesn't use them

这样你就得到了

-eo: -c: unexpected EOF while looking for matching `''
# the script named -eo, with the option -c having value 'ps,
# tried to find a closing ' to match the opening ' and it's not there

这 shell 显然是 (GNU) bash;许多将数据字符串放入错误消息中的 GNU 程序用反引号和撇号将其括起来,因为这些是几十年前流行的一种 ASCII 解释中的匹配引号。

而是使用 the String[] overload of exec 为 shell 提供当您的上述命令行被 解析时获得的两个参数 shell而不是 StringTokenizer:

 String[] cmdary = {"/bin/sh", "-c", "ps -eo stuff | fgrep this | fgrep that | fgrep -v fgrep"};
 ... Runtime.getRuntime().exec(cmdary);

但不是 运行 宁三个 fgrep,你可以只 运行 ps 并将输入流读取为行并在 Java 中使用 [=21 测试它们=] 或类似的。此外,您向 ps 请求的大多数列永远不会用于您的匹配 结果,因此这只是浪费精力和混乱。