1

我希望能够从 java 执行一个外部命令,例如“ls”,并以字符串的形式实时获取输出和错误流的输出,并按照它们生成的顺序。

因此,如果命令的输出类似于:

blah     <- to stdout
foo      <- to stderr
bar      <- to stdout

然后,理想情况下,我希望输出字符串和实时输出看起来像:

blah
foo
bar

朴素的方法会产生:

blah
bar

(即没有标准错误输出)

或者:

blah
bar
foo

(即重新排列顺序,使stdout 和stderr 消息不会相互交错)。

4

3 回答 3

3

使用ProcessBuilder和设置redirectErrorStream(true).

于 2012-10-12T13:39:26.723 回答
3

这并不总是可能的。

如果您使用ProcessBuilderAPI,您可以将stdoutstderr流合并为一个(使用redirectErrorStream(true)),因此您可以在一个InputStream. 但这意味着您无法分辨数据最初来自哪个流。

如果您将stdoutandstderr作为两个流读取,则需要 NIO 或两个 Java 线程。在这两种情况下,处理一个输出都会阻塞另一个(或者更确切地说,另一个流不会被及时处理)。这将导致换行。

如果子进程没有刷新输出并且您使用管道,那么情况会变得更糟,因为stdout将以 4KB 块的形式发送到您的进程,而stderr通常会逐行到达。

恐怕没有独立于平台的解决方法。如果您只需要适用于 Unix 的解决方案,您可以使用 Pseudo TTY ( man 4 pty) 来模拟 shell,但这些很难从 Java 中设置,因为您需要调用 OS 函数。

一种方法可能是使用 Perl 或类似方法在 PTY 中运行您的命令(这会导致 stdout 变为行缓冲),从那里读取 stdout 和 stderr 并在每行前面加上1for stdout 和2for stderr。

于 2012-10-12T13:44:44.487 回答
1

我建议您使用Apache commons exec API,因为它们是更复杂的 API。

请参阅DefaultExecutor:您可以:

  • 为子进程设置当前工作目录
  • 提供一组传递给子进程的环境变量
  • 使用 ExecuteStreamHandler 捕获 stdout 和 stderr 的子进程输出
  • 使用 ExecuteWatchdog 杀死长时间运行的进程
  • 定义一组预期的退出值
  • 当主进程使用 ProcessDestroyer 终止时终止任何已启动的进程
于 2012-10-12T13:47:37.730 回答