简体   繁体   English

使用 java 实时获取进程 output

[英]Get process output in real time with java

I would like to print live output of a process in java;我想在 java 中打印一个进程的实时 output; but the output is (almost) only available when the process stops execution.但是 output (几乎)仅在进程停止执行时才可用。

In other words, when I run the same process with the command line, I get more frequent feedback (output) than when I execute it with Java.换句话说,当我使用命令行运行相同的进程时,我得到的反馈(输出)比使用 Java 执行时更频繁。

I tested the following code to print the output every 250ms:我测试了以下代码以每 250 毫秒打印一次 output:

private static String printStream(BufferedReader bufferedReader) {
    try {
        String line = bufferedReader.readLine();
        if(line != null) {
            System.out.println(line);
        }
        return line;
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return null;
}

public static void sendProcessStreams(Process process, SendMessage sendMessage) {
    String[] command = { "/bin/bash", "-c", "custom_process"};
    ProcessBuilder processBuilder = new ProcessBuilder(command);
    processBuilder.directory(new File("."));
    Process process = processBuilder.start();

    BufferedReader inputBufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()), 1);
    BufferedReader errorBufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()), 1);
    System.out.println("Start reading process");
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
          @Override
          public void run() {
              String inputLine = printStream(inputBufferedReader);
              String errorLine = printStream(errorBufferedReader);
              if(inputLine == null && errorLine == null && !process.isAlive()) {
                  timer.cancel();
              }
          }
     }, 0, 250);
}

Yet it only prints two lines, and then waits for the process to end before printing everything else.然而它只打印两行,然后等待进程结束,然后再打印其他所有内容。

Is it possible to get more frequent feedback from an external process?是否有可能从外部流程获得更频繁的反馈? If not, Why not?如果不是,为什么不呢?

Your code basically (as far as reading from the output) works correctly, the problem is surely for another reason, please build a minimal problem that is reproducible (eg. what is custom_process , why you use Timer when you can use the current thread, ...).您的代码基本上(就从输出中读取而言)工作正常,问题肯定是出于另一个原因,请构建一个可重现的最小问题(例如,什么是custom_process ,为什么在可以使用当前线程时使用Timer , ...)。

Anyway, here's an example reading in realtime the output:无论如何,这是一个实时读取 output 的示例:

final Process proc = new ProcessBuilder(
            "/bin/bash", "-c",
            "for i in `seq 1 10`; do echo $i; sleep $((i % 2)); done")
           .start();
try(InputStreamReader isr = new InputStreamReader(proc.getInputStream())) {
    int c;
    while((c = isr.read()) >= 0) {
        System.out.print((char) c);
        System.out.flush();
    }
}

With output:使用 output:

1         (and wait one second)
2
3         (and wait one second)
4
5         (and wait one second)
6
7         (and wait one second)
8
9         (and wait one second)
10

it seems like you are dealing with multithreading-problems and not with getting the output of the process.似乎您正在处理多线程问题,而不是获取进程的 output。

I just made this demo class that you can use:我刚刚做了这个演示 class 你可以使用:

CommandExecTest.java CommandExecTest.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;

public class CommandExecTest {
    public static void main(String[] args) throws InterruptedException {

        String executable = "cmd";
        String[] commandParams = {"@ping -n 5 localhost","echo \"hello world\"","exit 123"};
        boolean passCommandsAsLinesToShellExecutableAfterStartup = true;
        AsyncExecutor asyncExecutor = new AsyncExecutor(executable, commandParams,passCommandsAsLinesToShellExecutableAfterStartup);
        System.out.println("x"+"/x\tsecs in main thread \t\t status:"+asyncExecutor.runstate+" of async thread that monitors the process");
        asyncExecutor.start();//start() invokes the run() method as a detached thread
        for(int i=0;i<10;i++) {
            // you can do whatever here and the other process is still running and printing its output inside detached thread
            Thread.sleep(1000);
            System.out.println(i+"/10\tsecs in main thread \t\t status:"+asyncExecutor.runstate+" of async thread that monitors the process");
        }

        asyncExecutor.join(); // main thread has nothing to do anymore, wait till other thread that monitor other process finishes as well
        System.out.println("END OWN-PROGRAMM: 0 , END OTHER PROCESS:"+asyncExecutor.processExitcode);
        System.exit(0);
    }
}

Runstate.java运行状态.java

public static enum Runstate {
    CREATED, RUNNING, STOPPED
}

AsyncExecutor.java AsyncExecutor.java

public static class AsyncExecutor extends Thread{
    private String executable;
    private String[] commandParams;
    public ArrayList<String> linesSoFarStdout = new ArrayList<>();
    public ArrayList<String> linesSoFarStderr = new ArrayList<>();
    public Runstate runstate;
    public int processExitcode=-1;
    private boolean passCommandsAsLinesToShellExecutableAfterStartup = false;
    public AsyncExecutor(String executable, String[] commandParams) {
        this.executable=executable;
        this.commandParams=commandParams;
        this.runstate=Runstate.CREATED;
        this.passCommandsAsLinesToShellExecutableAfterStartup=false;
    }
    /**
     * if you want to run a single-process with arguments use <b>false</b> example executable="java" commandParams={"-jar","myjarfile.jar","arg0","arg1"} 
     * <p>
     * if you want to run a shell-process and enter commands afterwards use <b>true</b> example executable="cmd" commandParams={"@ping -n 5 localhost","echo \"hello world\"","exit 123"} 
     * @param executable
     * @param commandParams
     * @param passCommandsAsLinesToShellExecutableAfterStartup
     */
    public AsyncExecutor(String executable, String[] commandParams, boolean passCommandsAsLinesToShellExecutableAfterStartup) {
        this.executable=executable;
        this.commandParams=commandParams;
        this.runstate=Runstate.CREATED;
        this.passCommandsAsLinesToShellExecutableAfterStartup=passCommandsAsLinesToShellExecutableAfterStartup;
    }
    @Override
    public void run() {
        this.runstate=Runstate.RUNNING;
        // 1 start the process
        Process p = null;
        try {
            if(passCommandsAsLinesToShellExecutableAfterStartup) {
                // open a shell-like process like cmd and pass the arguments/command after opening it
                // * example:
                // * open 'cmd' (shell)
                // * write 'echo "hello world"' and press enter
                p = Runtime.getRuntime().exec(new String[] {executable});
                PrintWriter stdin = new PrintWriter( p.getOutputStream());
                for( int i = 0; i < commandParams.length; i++) { 
                    String commandstring = commandParams[i];
                    stdin.println( commandstring);
                }
                stdin.close();
            }
            else {
                // pass the arguments directly during startup to the process
                // * example:
                // * run 'java -jar myexecutable.jar arg0 arg1 ...'
                String[] execWithArgs = new String[commandParams.length+1];
                execWithArgs[0] = executable;
                for(int i=1;i<=commandParams.length;i++) {
                    execWithArgs[i]=commandParams[i-1];
                }
                p = Runtime.getRuntime().exec( execWithArgs);
            }
            // 2 print the output
            InputStream is = p.getInputStream();
            BufferedReader br = new BufferedReader( new InputStreamReader( is));

            InputStream eis = p.getErrorStream();
            BufferedReader ebr = new BufferedReader( new InputStreamReader( eis));

            String lineStdout=null;
            String lineStderr=null;
            while(p.isAlive()) {
                Thread.yield(); // *
                // * free cpu clock for other tasks on your PC! maybe even add thread.sleep(milliseconds) to free some more
                // * everytime this thread gets cpu clock it will try the following codeblock inside the while and yield afterwards for the next time it gets cpu-time from sheduler
                while( (lineStdout = br.readLine()) != null || (lineStderr = ebr.readLine()) != null) {
                    if(lineStdout!=null) {
                        System.out.println(lineStdout);
                        linesSoFarStdout.add(lineStdout);
                    }
                    if(lineStderr!=null) {
                        System.out.println(lineStderr);
                        linesSoFarStderr.add(lineStderr);
                    }
                }
            }
            // 3 when process ends
            this.processExitcode = p.exitValue();
        }
        catch(Exception e) {
            System.err.println("Something went wrong!");
            e.printStackTrace();
        }
        if(processExitcode!=0) {
            System.err.println("The other process stopped with unexpected existcode: " + processExitcode);
        }
        this.runstate=Runstate.STOPPED;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM