如何从在单独的线程中运行的多个外部命令管理多个标准输出?

时间:2019-05-13 18:20:51

标签: java multithreading scala

我想从我的Scala程序中运行多个外部命令,并能够处理它们的输出(发送到stdout)。有办法让我做到吗?程序的输出将包含一些日志记录以及可能的进度信息。我需要解析它们的输出并将其发送到数据库服务器,以保存日志,进度和关闭过程(如果它们返回错误)。重要的一点是,我不想等待不同的过程完成,而要连续获取输出。

我通过这种方式运行单独的外部命令:

def execCommand(command: String, bId: Long): Future[(Long, Stream[String])] = {
    Future {
      bId -> command.lineStream
    }(executionContext)
  }

“ bId”只是我的进程ID。除了使用以下方法之外,我无法管理其他方法:

Await.result() 

方法,基于“ execCommand”方法的结果,但这不是我想要的方法。

我希望我的程序能够处理多个线程中的多个数据流,并连续地管理这些数据。不必是scala,用java解决方案也可以。

1 个答案:

答案 0 :(得分:0)

启动该进程时,还将启动一个线程以读取该进程的输出。

如果要在单个线程中管理该输出,请创建一个BlockingQueue并将输出发送到队列。您可能想跟踪输出来自哪个进程,因此在队列中使用POJO。

示例

public class CommandOutput {
    private final int process;
    private final String line;
    public CommandOutput(int process, String line) {
        this.process = process;
        this.line = line;
    }
    public int getProcess() {
        return this.process;
    }
    public String getLine() {
        return this.line;
    }
}
public class CommandOutputStreamHandler extends Thread {
    private final int process;
    private final InputStream stream;
    private final BlockingQueue<CommandOutput> queue;
    public CommandOutputStreamHandler(int process, InputStream stream, BlockingQueue<CommandOutput> queue) {
        this.process = process;
        this.stream = stream;
        this.queue = queue;
    }
    @Override
    public void run() {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(this.stream))) {
            for (String line; (line = reader.readLine()) != null; ) {
                this.queue.add(new CommandOutput(this.process, line));
                Thread.sleep(200); // just for simulating output over time
            }
        } catch (Exception e) {
            throw new RuntimeException(e); // or simply log the exception
        } finally {
            this.queue.add(new CommandOutput(this.process, null));
        }
    }
}
BlockingQueue<CommandOutput> queue = new LinkedBlockingQueue<>();

final int PROCS = 3;
for (int i = 0; i < PROCS; i++) {
    Process p = new ProcessBuilder("cmd.exe", "/c", "dir", "/b")
            .redirectErrorStream(true)
            .start();
    new CommandOutputStreamHandler(i, p.getInputStream(), queue)
            .start();
}

for (int endMarkers = 0; endMarkers < PROCS; ) {
    CommandOutput co = queue.take();
    if (co.getLine() == null) {
        endMarkers++;
    } else {
        System.out.println(co.getProcess() + ": " + co.getLine());
    }
}

示例输出

0: .classpath
0: .project
1: .classpath
0: .settings
1: .project
0: lib
1: .settings
0: pom.xml
1: lib
0: src
1: pom.xml
0: target
1: src
2: .classpath
1: target
2: .project
2: .settings
2: lib
2: pom.xml
2: src
2: target