在java中读取进程的实时输出

时间:2017-02-01 22:38:33

标签: java python stdout

我使用

在我的java应用程序中启动python脚本
Process p = Runtime.getRuntime().exec("python script.py");

此脚本在循环中运行,仅由事件(或用户交互)取消。脚本在每个循环周期写入输出,一些文本如" 12:22:35 - Heartbeat"

while True:
  print("Heartbeat")
  time.sleep(1)

在我的Java应用程序中,我希望在显示时读取此输出。我的问题是,如果我使用BufferReader,它将等到进程完成后再读取输出。以下是我的阅读方式:

BufferedReader is = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;

while ((line = is.readLine()) != null)
   System.out.println(line);

如何阅读输出" live"?

为了更好地理解:python-script监听硬件按钮,当按下该按钮时,会写出一些输出。在我的java应用程序中,我想显示此消息并将其发送给某些客户端。

4 个答案:

答案 0 :(得分:3)

使用https://github.com/zeroturnaround/zt-exec

new ProcessExecutor().command("python", "script.py")
    .redirectOutput(new LogOutputStream() {
        @Override
        protected void processLine(String line) {
            ...
        }
    })
    .execute();

答案 1 :(得分:1)

我经常写一些类似的东西:

class StreamHandler extends Thread {

    InputStream is;
    Writer writer;

    StreamHandler(InputStream is, Writer writer) {
        super("StreamHandler");

        this.is = is;
        this.writer = writer;
    }

    @Override
    public void run() {
        try (InputStreamReader isr = new InputStreamReader(is)) {

            char buffer[] = new char[256];
            int n;
            while ((n = isr.read(buffer)) > 0) {
                writer.write(buffer, 0, n);
            }
        } catch (IOException e) {
            System.err.println("StreamHandler: " + e);
        }
    }
}

虽然我已经将它用于捕获输出流到我自己的缓冲区,但您可以实时回显到stdout,如:

Process process = Runtime.getRuntime().exec(...);
Writer writer = new OutputStreamWriter(System.out);
StreamHandler stdout_handler = new StreamHandler(process.getInputStream(), writer);
stdout_handler.start();

答案 2 :(得分:0)

尝试将您的python代码更改为:

while True:
    print("Heartbeat", flush=True)
    time.sleep(1)

我一直在努力做同样的事情并在这个问题上找到了这个解决方案:PumpStreamHandler can capture the process output in realtime

答案 3 :(得分:0)

如果只想打印到当前进程的流,则可以使用ProcessBuilder类创建一个进程,并使其“继承” IO流,如下所示:

Process process = new ProcessBuilder()
            .command(commandList)
            .directory(workingDirectory)
            .inheritIO()
            .start();

或者,如果您想更好地控制流,

Process process = new ProcessBuilder()
            .command(commandList)
            .directory(workingDirectory)
            .redirectOutput(ProcessBuilder.Redirect.INHERIT)
            .redirectInput(ProcessBuilder.Redirect.INHERIT)
            .redirectError(ProcessBuilder.Redirect.INHERIT)
            .start();

同样,这可能会或可能不会适合您的情况,并且取决于程序正在运行的终端。我尝试在系统终端中使用Java程序运行mysqlpython命令行解释器,并且运行良好。

但是,如果您想将输出重定向到其他地方,则该进程的标准输出将被缓冲(请参见here)。并且在您的情况下,仅在程序完成时才刷新缓冲区。因此,像在答案中提到的@Mateus Terra之类的在flush=True中设置print的事情可能会起作用。