在java中执行脚本,读取错误和输出流

时间:2012-10-12 13:34:21

标签: java shell

我希望能够从java执行外部命令,例如" ls",并将输出,输出和错误流作为字符串,并实时获取,并按顺序生成了。

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

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

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

blah
foo
bar

天真的方法产生:

blah
bar

(即没有stderr输出)

或:

blah
bar
foo

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

3 个答案:

答案 0 :(得分:3)

使用ProcessBuilder并设置redirectErrorStream(true)

答案 1 :(得分:3)

这并非总是可行。

如果您使用ProcessBuilder API,则可以将stdoutstderr流合并为一个(使用redirectErrorStream(true)),这样您就可以在一个内容中读取两个输出InputStream。但这意味着您无法判断数据最初来自哪个流。

如果您将stdoutstderr视为两个流,则需要NIO或两个Java线程。在这两种情况下,处理一个输出将阻止另一个输出(或者更确切地说,不会及时处理另一个流)。这将导致交换线。

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

我担心没有独立于平台的解决方法。如果您只需要一个适用于Unix的解决方案,您可以使用伪TTY(man 4 pty)来模拟shell,但由于您需要调用OS函数,因此很难从Java设置。

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

答案 2 :(得分:1)

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

请参阅DefaultExecutor:您可以:

  • 设置子流程的当前工作目录
  • 提供一组传递给子流程的环境变量
  • 使用ExecuteStreamHandler捕获stdout和stderr的子进程输出
  • 使用ExecuteWatchdog
  • 终止长时间运行的进程
  • 定义一组预期退出值
  • 在主进程使用ProcessDestroyer终止时终止所有已启动的进程