【问题标题】:Java Process cannot get the InputStream through Runtime.getRunTime().exec()Java 进程无法通过 Runtime.getRunTime().exec() 获取 InputStream
【发布时间】:2013-01-21 13:31:01
【问题描述】:
try {

        String str;
        Process process = Runtime.getRuntime().exec("bash /home/abhishek/workspace/Pro/run");
        InputStream isout = process.getInputStream();
        InputStreamReader isoutr = new InputStreamReader(isout);
        BufferedReader brout = new BufferedReader(isoutr);
        while ((str = brout.readLine()) != null) {
            System.out.println(str);
        }

} catch (IOException e) {
        e.printStackTrace();
}

代码在从流程中获取 InputStream 时存在问题, 因为如果我从终端运行 Shell 脚本,它运行得非常好, 但如果我这样运行脚本,str 总是为空,

我正在使用此代码将 Shell 脚本的输出直接获取到 Java 中,而不是将脚本输出写入文件中

有没有其他方法可以实现这一点,或者如何使用当前方法解决问题

【问题讨论】:

  • 好问题,但很难。它与总是很痛苦的 IO 阻塞有关。但是,有一个名为 akka (akka.io) 的库声称可以克服此类 IO 阻塞问题。我已经开始学习它了,我不得不承认它也有它的局限性......
  • 您确定脚本打印到标准输出而不是标准错误吗?
  • process.getErrorStream() 是否有任何返回?

标签: java linux shell runtime bufferedreader


【解决方案1】:

在多次阅读https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/unix/classes/java/lang/ProcessImpl.javaProcess 的 unix 实现的源代码后,标准重定向似乎总是会吞并到 ProcessBuilder.NullInputStream

    if (redirects[1] == Redirect.PIPE) {
        std_fds[1] = -1;
    }...

    stdout = (fds[1] == -1 || forceNullOutputStream) ?
            ProcessBuilder.NullInputStream.INSTANCE :
            new ProcessPipeInputStream(fds[1]);

(对于 stdIn、stdOut 和 stdErr 流重复相同的代码)

我发现的唯一一种感觉很笨拙的解决方法是使用临时文件:

    File stdOutTmp;  // create and destroy however you see fit
    ProcessBuilder pb = ...;
    pb.redirectOutput(ProcessBuilder.Redirect.to(stdOutTmp));
    ...

还有其他静态工厂方法(Redirect.appendTo(File) 附加到现有文件而不是覆盖现有文件,Redirect.from(File) 用于 stdIn)

【讨论】:

    【解决方案2】:

    我认为某些内容是通过错误流返回的,因此您可以尝试从 Process.getErrorStream() 中检查某些内容。

    您还应该等待创建的进程以防止您的主程序在它之前完成。使用 Process.waitFor();

    public class TestMain {
       private static final String BASH_CMD = "bash";
    
       private static final String PROG = "/home/abhishek/workspace/Pro/run";
    
       private static final String[] CMD_ARRAY = { BASH_CMD , PROG };
    
       public static void main(String[] args) {
          new Thread(new Runnable() {
             public void run() {
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                      System.in));
                String command = null;
                try {
                   while ((command = reader.readLine()) != null) {
                      System.out.println("Command Received:" + command);
                   }
                } catch (Exception ex) {
                   ex.printStackTrace();
                   // failed to listening command
                }
    
             }
          }).start();
          Process process = null;
          try {
             ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
             process = processBuilder.start();
             InputStream inputStream = process.getInputStream();
             setUpStreamGobbler(inputStream, System.out);
    
             InputStream errorStream = process.getErrorStream();
             setUpStreamGobbler(errorStream, System.err);
    
             System.out.println("never returns");
             process.waitFor();
          } catch (IOException e) {
             throw new RuntimeException(e);
          } catch (InterruptedException e) {
             throw new RuntimeException(e);
          }
       }
    
       public static void setUpStreamGobbler(final InputStream is, final PrintStream ps) {
          final InputStreamReader streamReader = new InputStreamReader(is);
          new Thread(new Runnable() {
             public void run() {
                BufferedReader br = new BufferedReader(streamReader);
                String line = null;
                try {
                   while ((line = br.readLine()) != null) {
                      ps.println("process stream: " + line);
                   }
                } catch (IOException e) {
                   e.printStackTrace();
                } finally {
                   try {
                      br.close();
                   } catch (IOException e) {
                      e.printStackTrace();
                   }
                }
             }
          }).start();
       }
    }
    

    【讨论】:

      【解决方案3】:

      试试这样的:

      String[] runCommand = new String[3];
      
      runCommand[0] = "sh";
      runCommand[1] = "-c";
      runCommand[2] = "bash /home/abhishek/workspace/Pro/run";
      
      Process process = runtime.exec(runCommand);
      BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
      String line = reader.readLine();
      reader.close();
      

      【讨论】:

        【解决方案4】:

        可能的问题是当您获得 inputSram 时,子流程还没有准备好

        试试

        Process process = Runtime.getRuntime().exec("bash /home/abhishek/workspace/Pro/run");
        InputStream isout = process.getInputStream();    
        process.waitFor()
        

        【讨论】:

        • Runtime.getRuntime() 返回Runtime,而不是处理。 @TechExchange,我建议您在收到反对票之前删除您的答案。
        【解决方案5】:

        您的代码看起来不错。因此,我认为问题出在您正在使用的命令行中 (bash /home/abhishek/workspace/Pro/run) 或您的脚本本身。

        我建议您执行以下步骤:

        1. 尝试运行一些众所周知的命令而不是您的脚本。例如pwd。检查从输入流中读取的代码是否正常工作。
        2. 现在尝试简化您的脚本。创建脚本run1,它只运行相同的pwd。现在从 java 运行这个脚本,看看它是否正常工作。顺便说一句,您不必以bash yourscript 运行它。不用bash前缀就可以直接运行
        3. 如果所有这些工作开始逐步从简单到真正的脚本。我相信你会发现你的错误。可能您的脚本因某些与环境相关的问题而无法启动。

        【讨论】:

        • 嘿,我刚刚发现我的输出依赖于我通过脚本运行的 awk 程序,它在终端上打印输出,但是它不会通过运行时将其返回到进程,所以是有没有办法将 awk 输出传送到 shell 中的变量中?我像vmstat | awk -f xx.awk 一样使用它,输出由 awk 文件使用 print 语句打印,我现在需要将它放入 shell 脚本中的变量中,您的建议有所帮助,我需要最后的帮助。
        【解决方案6】:

        编辑你/home/abhishek/workspace/Pro/run如果它是一个shell并在顶部添加以下行。

        #!/usr/bin/bash
        

        并将所需的执行权限授予/home/abhishek/workspace/Pro/run

        然后使用下面一行

        Process process = Runtime.getRuntime().exec("/home/abhishek/workspace/Pro/run");
        

        现在,如果运行程序打印任何内容,您应该会在输出中看到它。

        【讨论】:

        • 在 linux 中,如果您正在执行程序,“./run”和“run”可能会出现问题。行如:Process process = Runtime.getRuntime().exec("/home/abhishek/workspace/Pro/./run");没有帮助吗?
        • ./ 仅用于引用相对路径。但是由于我们提供了像这样的绝对路径 /home/abhishek/workspace/Pro/run ,所以它不是必需的。
        • 正如我所说,我正在运行一个 Shell 脚本,所以它根本不能是 ./run,如果有任何东西 run.sh 会使问题更加明显。
        猜你喜欢
        • 2011-03-10
        • 2012-10-14
        • 1970-01-01
        • 2014-05-24
        • 2011-01-07
        • 2012-06-21
        • 2017-01-03
        • 1970-01-01
        相关资源
        最近更新 更多