【问题标题】:Invoking cmd commands from Java using processbuilder使用 processbuilder 从 Java 调用 cmd 命令
【发布时间】:2017-09-18 21:29:23
【问题描述】:

我正在尝试使用 processbuilder 从 Java 调用 cmd 命令。但是我面临的问题很少。

  1. 当我使用使用 Arrays.asList 构建的 List 时,应用程序在执行 br.readline() 后无限挂起(不是因为循环,而是在 readLine 方法中)。使用字符串数组给出输出。我检查了 grepcode,它看起来应该没有问题,因为当从 processbuilder 调用 start 方法时,它们都被转换回数组。 (链接:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/lang/ProcessBuilder.java#ProcessBuilder)。我不确定是什么导致了这个异常。

  2. 此程序有时无法提供正确的输出。如果我使用 process.destroy() 方法并获取 exitValue,它会显示 1。如果我注释掉 process.destroy() 方法,则会出现进程未退出的异常。我想这可能是线程竞争条件。但是,没有 process.waitFor() 的影响。它有时仍然会产生错误的输出。我们如何测试这些情况并找出问题的真正原因?

  3. 我需要在 cmd 中调用一些命令。但是,数组元素充当前一个元素的参数。例如,如果我使用 cmd /C dir whoami 的元素制作数组。然后这会产生错误的输出,因为 whoami 充当 dir 的参数。向cmd独立提供命令的正确方法是什么?

以下是有相同问题的示例代码:

   import java.io.BufferedReader;
   import java.io.InputStreamReader;
   import java.util.Arrays;
   import java.util.List;
   public class Sample {
       public static void main(String[] args) throws Exception {
        //List<String> commandList = Arrays.asList("cmd.exe","dir");
        String[] commandList = {"cmd.exe", "/C", "dir"};
        //String[] commandList = {"cmd.exe", "/C", "dir", "whoami"};
        //String[] commandList = new String[] {"cmd.exe", "/C", "dir"};
        ProcessBuilder processBuilder = new ProcessBuilder(commandList);
        Process process = processBuilder.start();
        //process.waitFor();
        BufferedReader iReader = new BufferedReader(
                             new InputStreamReader(process.getInputStream()));
        String tempStr= "";
        StringBuffer buffer = new StringBuffer();
        while((tempStr = iReader.readLine())!=null){
            buffer.append(tempStr+System.lineSeparator());
        }
        System.out.println(buffer.toString());
        process.destroy();
        int exitValue = process.exitValue();
        System.out.println(exitValue);
    }
   }

【问题讨论】:

    标签: java processbuilder


    【解决方案1】:
    1. Arrays.asList("cmd.exe","dir") 有两个 List 元素。 ("cmd.exe", "dir") 与 ("cmd.exe", "/C", "dir") 不同。它可能挂起,因为您忽略了包含描述错误的错误消息的错误输出。您可以使用 processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); 解决此问题(这通常是一个好主意,除非您打算以其他方式读取错误流)。
    2. 删除 process.destroy() 并将 process.exitValue() 替换为 process.waitFor()在阅读所有输出之前不要调用 process.waitFor(); waitFor() 等待进程结束,您无法读取不再运行的进程的输出。
    3. 您可以在命令之间使用&amp;&amp; 链接命令;例如:new ProcessBuilder("cmd.exe", "/C", "dir &amp;&amp; date /t")。如果这不起作用,您可以尝试创建一个临时 .bat 文件并将其传递给 cmd /c。如果这不可接受,您可能必须为要运行的每个命令创建一个单独的进程。

    顺便说一下,StringBuffer 已经过时了。请改用 StringBuilder,因为它没有不必要的同步开销。

    【讨论】:

    • 啊!!!我没有意识到这个问题是通过缺少“/ C”开关来完成的,该开关在完成后会自动终止。感谢 cmd 中的“&&”命令。不幸的是,这些命令不是固定的,而是根据标准动态生成的。所以脚本在这里不太可行。我将尝试重新定位 process.waitFor 并回复您。同时,是否有任何方法可以测试线程竞争或创建进程结束过快或需要很长时间的场景,以便我们检查它在结束场景中的工作方式。
    • 您可以将动态命令写入扩展名为“.bat”的新文件,并将该文件名传递给 cmd /c。 Files.createTempFileFiles.write 对此很有用。
    • 是的,这涉及到创建一个临时文件。也提出了这个想法,但我的团队不赞成 IO。第二个问题是获取临时存储的位置访问权限。但只要“&&”有效,它就很好。否则我会尝试一个接一个地运行多个进程。最后一件事,在这个程序运行之前,我们正在检查主机操作系统。所以我们将调用终端以防它的 linux 操作系统。我已经在在线终端中尝试过 && 命令,它看起来不错,但我需要确保它适用于所有命令,而不是基本的 ls、whoami 等。它将是 debian 或其变体
    • 我相信我可能没有必要的权限来接受答案,或者可能已经让它回来讨论更多。我只是在回顾我的旧代码时重新访问了这篇文章。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    相关资源
    最近更新 更多