【发布时间】:2014-01-08 18:25:04
【问题描述】:
在为控制台应用程序构建包装器时,我遇到了一个奇怪的问题,即连接到外部进程的输出 (stdout) 的输入流完全为空白直到外部进程退出。 p>
我的代码如下:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
public class Example{
public static void main(String args[]) throws IOException{
File executable = ...
ProcessBuilder pb = new ProcessBuilder(executable.getCanonicalPath());
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")));
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
}
我尝试了几种从输入流中读取的变体,结果都是相同的。
我试过了:
CharBuffer charBuf = CharBuffer.allocate(1000);
InputStreamReader isr = new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"));
while(isr.read(charBuf) != -1){
System.out.print(charBuf.flip().toString());
}
和
byte[] buf = new byte[1000];
int r;
while((r = p.getInputStream().read(buf)) != -1){
System.out.print(new String(buf, 0, r));
}
一切都无济于事。
外部进程的输出被缓冲(无限期地),我真的不知道在哪里。从命令行加载进程似乎工作正常,我看到输出立即出现。最奇怪的部分是,外部进程的终止会导致所有“缓冲”输出一次泛滥(很多东西)。
不幸的是,我无法访问外部进程的源代码,但考虑到它在控制台中写入标准输出时可以正常工作(据我所知),这并不会真正产生影响。
欢迎提出任何想法。
编辑:
一个答案建议我重写阅读器以使输出和错误流在单独的线程上运行。 我的实际实现是这样做的!但问题仍然存在。上面发布的代码是我的实际代码的SSCCE,出于可读性目的而压缩,实际代码涉及一个单独的线程,用于从InputStream读取。
编辑 2:
用户FoggyDay 似乎提供了答案,该答案定义了在控制台和非控制台之间输出时输出缓冲的行为如何变化。虽然检测到它们正在写入控制台的进程使用行缓冲(缓冲刷新每个新行),但写入非控制台(它检测到不是控制台的所有内容)可能会被完全缓冲(大小约为 8K )。如果我使外部进程垃圾邮件(for 循环中的 8K lorem ipsum)输出确实会出现。我想我现在的问题是如何让我的 java 程序在外部进程上触发行缓冲。
【问题讨论】:
-
如果您不想缓冲,为什么要使用
BufferedReader?我可能会改用java.util.Scanner。 -
@ElliottFrisch 最初我想要某种形式的缓冲,但鉴于效果不佳,我开始使用
CharBuffer并尝试解决问题。我的最后一次尝试是直接读取原始 InputStream,但仍然看不到任何输出。鉴于 Scanner 构建在原始 InputStream 之上,我不知道它会如何工作,但无论如何我都会尝试一下。 -
我假设您已经使用其他可执行文件尝试过您的代码,并在流程结束之前看到其输出可用?这个可执行文件有没有可能做一些时髦的事情,比如检查输出是否被重定向?
-
@MiserableVariable 是的,我有,它适用于大多数进程。但是您会看到可执行文件是否正在检查输出重定向并以某种方式阻止它,那么为什么在应用程序关闭时输出垃圾邮件?我知道这很可能是应用程序正在做的事情,我想知道的是什么。究竟是什么可能应用程序会导致这种行为。 stdio 不应该是一个非常简单的基础设施吗?为什么所有这些问题...
-
为什么这个问题当然很难回答。例如,可能是因为应用程序不想被重定向 :) 尝试替代实现可能很有用,如果您使用的是 unixish 操作系统,那么通过
| while read line; do echotime: $line; done进行管道传输可能会快速而肮脏测试显示它产生输出的频率。
标签: java inputstream processbuilder external-process