具有input/输出stream的Java进程

我有下面的代码示例。 因此,您可以input一个命令到bash shell,即echo test ,并将结果回显。 但是,在第一次阅读之后。 其他输出stream不起作用?

为什么这样做还是我做错了什么? 我的最终目标是创build一个Threaded计划任务,定期执行一个命令到/ bash,这样OutputStreamInputStream将不得不协同工作,而不是停止工作。 我也遇到了错误java.io.IOException: Broken pipe任何想法?

谢谢。

 String line; Scanner scan = new Scanner(System.in); Process process = Runtime.getRuntime ().exec ("/bin/bash"); OutputStream stdin = process.getOutputStream (); InputStream stderr = process.getErrorStream (); InputStream stdout = process.getInputStream (); BufferedReader reader = new BufferedReader (new InputStreamReader(stdout)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin)); String input = scan.nextLine(); input += "\n"; writer.write(input); writer.flush(); input = scan.nextLine(); input += "\n"; writer.write(input); writer.flush(); while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); } input = scan.nextLine(); input += "\n"; writer.write(input); writer.close(); while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); } 

首先,我会build议更换这一行

 Process process = Runtime.getRuntime ().exec ("/bin/bash"); 

与线

 ProcessBuilder builder = new ProcessBuilder("/bin/bash"); builder.redirectErrorStream(true); Process process = builder.start(); 

ProcessBuilder是Java 5中的新function,可以使运行外部进程变得更简单。 在我看来,它比Runtime.getRuntime().exec()最重要的改进是它允许你将subprocess的标准错误redirect到它的标准输出。 这意味着你只有一个InputStream读取。 在此之前,您需要有两个独立的线程,一个读取stdout和一个读取stderr ,以避免在标准输出缓冲区为空(导致subprocess挂起)时标准错误缓冲区填充,反之亦然。

接下来,循环(你有两个)

 while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); } 

只有当从进程的标准输出读取的读取reader返回文件结束时才退出。 只有当进程退出时才会发生这种情况。 如果目前发生的情况是不能再返回文件结束的话, 相反,它将等待进程的下一行输出,并且不会返回到下一行。

由于您在到达此循环之前将两行input发送到进程,因此如果在这两行input之后进程没有退出,则这两个循环中的第一个将挂起。 它会坐在那里等待另一条线被读取,但是永远不会有另一条线被读取。

我编译了你的源代码(目前我在Windows上,所以我用cmd.exereplace了/bin/bash ,但原理应该是一样的),我发现:

  • 在input两行后,出现前两个命令的输出,但程序挂起,
  • 如果我inputecho test ,然后exit ,程序就会从cmd.exe进程退出后的第一个循环中删除它。 程序然后要求input另一行(被忽略),直接跳过第二个循环,因为subprocess已经退出,然后退出。
  • 如果我inputexit ,然后echo test ,我得到一个IOExceptionexception抱怨closurespipe道。 这是可以预料的 – 第一行input导致进程退出,无处可送第二行。

我曾经在一个我曾经工作过的程序中看到过一个类似于你想要的东西的技巧。 这个程序围绕着许多shell,在其中运行命令,并从这些命令中读取输出。 使用的技巧是总是写出一个标志shell命令输出结束的“魔术”线,并用它来确定发送到shell的命令的输出何时完成。

我拿了你的代码,并用代码分配给writer的行之后,用下面的循环replace了所有的东西:

 while (scan.hasNext()) { String input = scan.nextLine(); if (input.trim().equals("exit")) { // Putting 'exit' amongst the echo --EOF--s below doesn't work. writer.write("exit\n"); } else { writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n"); } writer.flush(); line = reader.readLine(); while (line != null && ! line.trim().equals("--EOF--")) { System.out.println ("Stdout: " + line); line = reader.readLine(); } if (line == null) { break; } } 

做完这些之后,我可以可靠地运行一些命令,并将每个命令的输出单独回送给我。

发送到shell的两行echo --EOF--命令可以确保命令的输出以--EOF--结尾,即使是命令错误的结果。

当然,这种方法有其局限性。 这些限制包括:

  • 如果我input一个等待用户input的命令(例如另一个shell),程序似乎挂起,
  • 它假定shell运行的每个进程以一个换行符结束它的输出,
  • 如果shell运行的命令恰好写出了一行--EOF-- ,会有点困惑。
  • bash报告一个语法错误,如果input一些不匹配的文本,就会退出)

这些点对你来说可能无关紧要,无论你是否按计划任务运行,都将被限制在一个命令或一组永远不会以这种病态方式运行的命令。

编辑 :改善退出处理和其他在Linux上运行这个小的变化。

我认为你可以像使用demon-thread这样的线程来读取你的input,而你的输出阅读器已经在主线程的while循环中,这样你就可以同时读写了。你可以像这样修改你的程序:

 Thread T=new Thread(new Runnable() { @Override public void run() { while(true) { String input = scan.nextLine(); input += "\n"; try { writer.write(input); writer.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ); T.start(); 

你可以读者将与上述相同,即

 while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); } 

使你的作家成为最后的,否则它将无法通过内部类访问。

你有writer.close(); 在你的代码中。 所以bash在它的stdin接收EOF并退出。 然后,当你试图从已经停止的bash的stdout中读取的时候,你会看到Broken pipe