在构建控制台应用程序的包装器时,我遇到了一个奇怪的问题:连接到外部进程输出(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的lorem ipsum),确实会出现输出。我现在的问题是如何使我的Java程序触发外部进程的行缓冲。
BufferedReader
?我可能会改用java.util.Scanner
。 - Elliott FrischCharBuffer
等方法来尝试解决问题。我的最后一次尝试是直接读取原始的 InputStream,但仍然没有输出可见。鉴于 Scanner 是建立在原始 InputStream 之上的,我不知道它是否会奏效,但我还是会尝试一下。 - initramfs| while read line; do echo
time: $line; done
进行管道传输,这可能是一个快速而简单的测试,可以显示它产生输出的频率。 - Miserable Variable