在为控制台应用程序构建包装器时,我遇到了这个奇怪的问题,其中连接到外部进程的输出(stdout)的输入流完全空白,直到外部进程退出。
我的代码如下:
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));
}
一切都无济于事。
沿着这条线的某个地方,外部进程的输出被缓冲(无限期),我无法弄清楚在哪里。从命令行加载进程似乎工作正常,我看到输出即时出现。最奇怪的部分是外部过程终止导致所有“缓冲”输出立即泛滥的事实(很多东西)。
不幸的是我无法访问外部进程的源代码,但是如果它在控制台中写入stdout就不会在那里真正有所作为(据我所知)。
欢迎任何想法。
修改
一个答案建议我重写读取器以使输出和错误流在单独的线程上运行。 我的实际实现正在这样做!然而问题仍然存在。上面发布的代码是我的实际代码的SSCCE,为了可读性目的而浓缩,实际代码涉及一个单独的线程,用于从InputStream
读取。
编辑2:
用户FoggyDay似乎提供了答案,该答案定义了在控制台和非控制台之间输出时输出缓冲的行为如何变化。虽然检测到他们正在写入控制台的进程使用行缓冲(每个新行缓冲刷新),但写入非控制台(它检测到的不是控制台的所有内容)可以完全缓冲(大小等于8K的大小) )。如果我将外部进程垃圾邮件(for循环中的8k of lorem ipsum)输出确实出现了。我想我现在的问题是如何让我的java程序触发外部进程的缓冲。
答案 0 :(得分:1)
关于你的问题"如何让我的java程序触发外部进程的缓冲行":
在Linux上,您可以使用" stdbuf"程序(coreutils包): stdbuf -oL your_program program_args
您只需要更改stdout,因为默认情况下stderr是无缓冲的。如果您有兴趣,setlinebuf的手册页会提供其他背景信息:http://linux.die.net/man/3/setlinebuf
答案 1 :(得分:-1)
某些软件会检查它是否正在写入终端并将行为切换为无缓冲输出。这可能是一个原因,为什么它在终端工作。将输出传递给cat并查看输出是否仍然立即显示。
另一个原因可能是程序在执行某事之前等待输入或关闭stdin,尽管这与目前描述的症状并不完全匹配。