微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

完全按照写入的方式读取外部进程的交错标准输出和标准错误

如何解决完全按照写入的方式读取外部进程的交错标准输出和标准错误

我正在使用 ProcessBuilder 来启动 bash 进程。 我正在执行的命令写入 stdout 和 stderr。 到目前为止,我还没有找到一种方法来按照命令打印的顺序获取 stdout 和 stderr,以及在 Java 之外执行命令时它们的显示方式。

假设进程有这样的输出

  • 一行 (A) 写入标准输出
  • 一行 (B) 写入标准错误
  • 一行 (C) 写入标准输出

我想知道这些行的确切顺序。所以例如。 B 写在 C 之前,但写在 A 之后。

https://stackoverflow.com/a/38764695/4266296 中提供的答案似乎适用于某些人,但可能无法保证适用于所有平台? 我在 Ubuntu 20.04 上使用 OpenJDK 64 位服务器 VM AdoptOpenJDK(构建 11.0.8+10,混合模式)。

到目前为止我尝试过的:

  • processBuilder.redirectErrorStream(true): process.getInputStream() 以所有 stderr 行开始,然后是 stdout,如果我将 stdout 重定向File
  • processBuilder.inheritIO():直接打印到父级的控制台,但仍然没有按照消息写入的顺序(从 stdout 开始,然后是 stderr)
  • 2>&1 添加到命令中:与 redirectErrorStream(true) 的行为相同

解决方法

John Kugelman 的评论为我指明了正确的方向。非常感谢!

缓冲不会发生在 Java 端,而是通过管道 stdout 和 stderr 到另一个程序的结果。在本例中为 JVM。

它变成了 full buffered。另一方面,stderr 即使在管道传输时也保持行缓冲。

所以我们想改变外部进程调用的缓冲行为。

我选择了 https://unix.stackexchange.com/a/61833/274368 中描述的 script 路线。 我的代码现在看起来像这样:

String command = "script -q -c \"my-command\" /dev/null";
ProcessBuilder processBuilder = fs.runInShell(command,args.toArray(new String[args.size()]));
processBuilder.directory(workingDirectory);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
try (BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
    String message = input.lines().collect(Collectors.joining(System.lineSeparator()));
    // ...
}

其中 message 包含以正确顺序来自 stderr 和 stdout 的所有行。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。