![](/img/trans.png)
[英]java could not read output of python process until close the input stream
[英]Java Process with Input/Output Stream
我下面有以下代码示例。 您可以在其中输入bash shell的命令,即echo test
并将结果回显。 但是,先读后。 其他输出流不起作用?
为什么会这样或我做错了什么? 我的最终目标是创建一个线程计划任务,该任务定期执行对/ bash的命令,因此OutputStream
和InputStream
必须串联工作而不停止工作。 我也遇到了java.io.IOException: Broken pipe
错误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);
}
首先,我建议更换线
Process process = Runtime.getRuntime ().exec ("/bin/bash");
与线
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
ProcessBuilder是Java 5中的新增功能,使运行外部进程更加容易。 我认为,它对Runtime.getRuntime().exec()
最大改进是,它允许您将子进程的标准错误重定向到其标准输出中。 这意味着您只有一个InputStream
要读取。 在此之前,您需要有两个单独的线程,一个从stdout
读取,一个从stderr
读取,以避免在标准输出缓冲区为空(导致子进程挂起)时填充标准错误缓冲区,反之亦然。
接下来,循环(其中有两个循环)
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
仅当从流程的标准输出读取的reader
返回文件末尾时,才退出。 这仅在bash
进程退出时发生。 如果当前碰巧不再有该过程的输出,它将不会返回文件结束。 相反,它将等待流程的下一行输出,直到有下一行才返回。
由于您要在到达此循环之前向流程发送两行输入,因此,如果在这两行输入之后流程尚未退出,则这两个循环中的第一个将挂起。 它会坐在那里等待另一行被读取,但是永远不会再有另一行可以读取。
我编译了您的源代码(此刻我在Windows上,所以我用cmd.exe
替换了/bin/bash
,但是原理应该是相同的),我发现:
echo test
,然后exit
,则由于cmd.exe
进程已退出,该程序会将其退出第一个循环。 然后,程序要求输入另一行(将被忽略),由于子进程已经退出,因此直接跳过了第二个循环,然后退出自身。 exit
,然后执行echo test
,则会收到IOException,抱怨管道被关闭。 这是可以预期的-输入的第一行导致进程退出,并且无处发送第二行。 在我以前开发的程序中,我已经看到了一种技巧,该技巧与您想要的功能相似。 该程序保留了许多shell,在其中运行命令并从这些命令中读取输出。 所使用的技巧是始终写出一个“魔术”行来标记shell命令输出的结束,并使用该行来确定发送到shell的命令的输出何时完成。
我获取了您的代码,并使用以下循环替换了分配给writer
的行之后的所有内容:
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--
终止,即使在命令出错的情况下也是如此。
当然,这种方法有其局限性。 这些限制包括:
--EOF--
行,这--EOF--
。 bash
,如果你输入一些文字拥有无与伦比的报告一个语法错误并退出)
。 如果您要考虑的是计划任务中要执行的任务,那么这些要点可能对您而言并不重要,因为这些任务将仅限于一个命令或一小部分命令,这些命令或行为永远不会以这种病理方式表现。
编辑 :在Linux上运行此命令后,改善退出处理和其他较小更改。
我认为您可以使用诸如demon-thread之类的线程来读取输入,并且您的输出阅读器已经在主线程的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
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.