【问题标题】:No input from ProcessBuilder's InputStream until external process is closed在外部进程关闭之前,没有来自 ProcessBuilder 的 InputStream 的输入
【发布时间】: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 echo time: $line; done 进行管道传输可能会快速而肮脏测试显示它产生输出的频率。

标签: java inputstream processbuilder external-process


【解决方案1】:

对于您的问题“如何让我的 java 程序在外部进程上触发行缓冲”:

在 Linux 上,您可以使用“stdbuf”程序(coreutils 包):stdbuf -oL your_program program_args

您只需要更改标准输出,因为标准错误默认情况下是无缓冲的。如果您有兴趣,setlinebuf 的手册页会提供额外的背景信息:http://linux.die.net/man/3/setlinebuf

【讨论】:

  • 我的应用程序是跨平台的,因此特定于 linux 的解决方案还不够好。我会看看 stdbuf 的来源,看看是否有什么我可以用它做的。
  • 在windows下试过了(git自带stdbuf.exe),好像不行,估计是JDK缓冲了?
【解决方案2】:

某些软件会检查它是否正在写入终端并将行为切换到无缓冲输出。这可能是它在终端中工作的原因。将输出通过管道传递给 cat,看看输出是否仍然立即出现。

另一个原因可能是程序在执行某些操作之前正在等待输入或关闭其标准输入,尽管这与目前所描述的症状并不相符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多