Java:除非手动刷新,否则无法从进程获取标准输出数据

发布于 2024-10-03 07:04:37 字数 935 浏览 5 评论 0原文

我正在用 Java 为命令行程序编写一个终端包装器,并使用 ProcessBuilder 生成子进程。要将击键发送到子进程,我只需从 GUI 直接将 e.getKeyChar() 写入由 proc.getOutputStream() 给出的 OutputStream 。为了接收子进程的输出,我基本上有一个 while 循环,从子进程的 stdout 读取:

while ((b = br.read()) != -1) {
    System.out.println("Read "+b);
    bb[0] = (byte) b;
    // call an event listener with the read byte
    listener.dataReceived(bb);
}

这有效,如果我立即刷新两个<上的输出/strong> 结束。也就是说,我必须刷新每个用户输入,并且子进程必须刷新其自己的 stdout 才能使事情发生。否则,read() 会阻塞,等待数据,而数据实际上从未发送(子进程的 stdout 只是保持缓冲)。我怎样才能让 I/O 运行?

示例终端子进程:

#include <stdio.h>

int main() {
    char c;
    while((c = getchar()) != -1) {
        printf("Got: %d\n", c);
        // doesn't work in my Java program if the next line isn't present
        fflush(stdout);
    }
    return 0;
}

我在 Ubuntu 10.10 和 Sun Java 6 上运行。

I'm writing a terminal wrapper for a command-line program in Java, and I spawn the subprocess using ProcessBuilder. To send keystrokes to the subprocess, I just write e.getKeyChar() from the GUI straight to the OutputStream given by proc.getOutputStream(). To receive output from the subprocess, I basically have a while loop that reads from the subprocess's stdout:

while ((b = br.read()) != -1) {
    System.out.println("Read "+b);
    bb[0] = (byte) b;
    // call an event listener with the read byte
    listener.dataReceived(bb);
}

This works, only if I immediately flush the output on both ends. That is, I have to flush every user input and the subprocess has to flush its own stdout in order for stuff to happen. Otherwise, read() blocks, waiting for data, which is never actually sent (subprocess' stdout just keeps buffering). How can I get I/O going?

Example terminal subprocess:

#include <stdio.h>

int main() {
    char c;
    while((c = getchar()) != -1) {
        printf("Got: %d\n", c);
        // doesn't work in my Java program if the next line isn't present
        fflush(stdout);
    }
    return 0;
}

I'm running on Ubuntu 10.10 with Sun Java 6.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

千秋岁 2024-10-10 07:04:37

在数据写入磁盘之前,您无法从文件中读取数据。
在数据放入管道/套接字的缓冲区之前,您无法从套接字或管道读取数据。

您的 java 程序无法控制(*)外部进程何时刷新其输出并将数据写入磁盘/管道缓冲区/套接字缓冲区。您完全受外部程序缓冲行为的支配。对于每个操作系统和每种编程语言都是如此。

每个网络程序员都必须处理这个问题,所以就处理它吧。

(*) - 有时某些程序(例如 cat)具有选项 (-u) 来指示程序使用无缓冲输出。否则你就怜悯

You cannot read data from a file until the data has been written to the disk.
You cannot read data from a socket or pipe until the data has been put in the pipe/socket's buffer.

Your java program has no control(*) over when an external process flushes its output and writes data to the disk/pipe buffer/socket buffer. You are totally at the mercy of the buffering behavior of the external program. This is true on every operating system and in every programming language.

Every network programmer has to deal with this, so just deal with it.

(*) - Occasionally some programs (like cat for one) have options (-u) to instruct the program to use unbuffered output. Otherwise you are mercy of the

執念 2024-10-10 07:04:37

许多运行时库(例如,我知道 libc 会这样做,如果其他库也这样做,我也不会感到惊讶)默认情况下会缓冲其输出,例外当输出到终端时。当处理很多行时(例如,在正常的管道中),这极大地提高了数据处理的效率,但是当只有少量信息时,它会带来很大的伤害。如果您有权访问子进程的源代码,那么最好通过关闭缓冲或添加刷新来更新代码。

但这并不总是可能的,尤其是在处理第三方代码时。在这种情况下,我所知道的最好的其他修复方法是使用类似 期望欺骗子进程。在内部,Expect 知道如何假装是一个终端(在 Unix 上使用 ptys,在 Windows 上使用可怕的 hack),从而欺骗其他程序关闭(或至少减少)它们的缓冲。 Expect 有一个脚本 - unbuffer - 使其专门专注于此类用途。 (一般来说,它可以做的不仅仅是处理不规则的缓冲,但无论如何它是最好的解决方案。)

Many many runtime libraries (e.g., I know that libc does this, and wouldn't be at all surprised if others do too) will buffer their output by default except when the output is to a terminal. This enormously increases the efficiency of data handling when dealing with many lines (e.g., in a normal pipeline) but when there is only a small amount of information, it hurts a lot. If you have access to the source of the subprocess, it's definitely best to update the code by turning off buffering or adding flushes.

But that's not always possible, especially when dealing with third-party code. The best other fix I know of in that case is to use a tool like Expect to trick the subprocess. Internally, Expect knows how to pretend to be a terminal (using ptys on Unix and godawful hacks on Windows) so tricking the other programs into turning off (or at least reducing) their buffering. There is a script – unbuffer – for Expect that makes it focus specifically on this sort of use. (In general it can do a lot more than just dealing with unruly buffering, but it's the best fix anyway.)

梦年海沫深 2024-10-10 07:04:37

您没有从事件调度线程运行 I/O 读取循环,是吗?

您应该在单独的线程中运行从子进程读取的 I/O(如果您还没有这样做)。每个 GUI 按键立即刷新到子进程可能是最好的;除非你想支持某种“一次读取一整行”的东西。

You aren't running your I/O read loop from the event dispatch thread are you?

You should run the I/O read from the sub-process in a separate thread (if you are not already doing this). Immediate flush to the subprocess per GUI key-press is probably best; unless you want to support some kind of 'read a whole line at a time' thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文