2

I start a python script in my java application with

Process p = Runtime.getRuntime().exec("python script.py");

This script runs in a loop and is only canceled by an event (or user interaction). The script writes to the output every loop cycle, some text like "12:22:35 -- Heartbeat"

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

In my Java application I want to read this output as it appears. My problem is, if I use the BufferReader, it will wait until the process is completed and after that it reads the output. Here is how I read it:

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

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

How can I read the output "live"?

For better understanding: the python-script listen on a hardware button, and when this button is pressed, some output is written out. And in my java application I want to show up this message and sent it to some clients.

AJNeufeld
  • 8,526
  • 1
  • 25
  • 44
Marco Rehmer
  • 1,033
  • 2
  • 12
  • 32
  • Possible duplicate of http://stackoverflow.com/questions/13530429/read-realtime-output-of-a-command – Alex Feb 01 '17 at 23:00

4 Answers4

5

Using https://github.com/zeroturnaround/zt-exec

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

I've often written something like:

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);
        }
    }
}

While I've used this for capturing the output stream to my own buffer, you could simply echo to stdout in real time like:

Process process = Runtime.getRuntime().exec(...);
Writer writer = new OutputStreamWriter(System.out);
StreamHandler stdout_handler = new StreamHandler(process.getInputStream(), writer);
stdout_handler.start();
AJNeufeld
  • 8,526
  • 1
  • 25
  • 44
  • nice approach, but the stream handler waits until the process ends. The problem is that the process "never" ends (only if a given time is reached, for example 2 minutes). But I need to read the output lines directly when they appears. – Marco Rehmer Feb 04 '17 at 17:39
0

try changing your python code to:

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

I've been trying to do the same as you and found this solution on this question: PumpStreamHandler can capture the process output in realtime

Mateus Terra
  • 159
  • 1
  • 9
0

If all you want to do is to print to the stream of the current process, then you can use the ProcessBuilder class to create a process and make it "inherit" the IO streams like so:

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

Or, if you want finer control over the streams,

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

Again, this may or may not work for your case and that depends on the terminal where the program is running. I've tried running mysql and python command line interpreter with the java program in the system terminal and it worked fine.

But if you want to redirect output to somewhere else, then the stdout from the process becomes buffered (see here). And the buffer is only flushed during program completion in your case. So something like setting flush=True in print like @Mateus Terra mentioned in the answer might work.