首页 > 解决方案 > ProcessBuilder 的输入流为空,具体取决于操作系统

问题描述

我编写了这段简单的代码来测试 ProcessBuilder:

@SpringBootApplication
public class TerminalDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(TerminalDemoApplication.class, args);
        try {
            System.out.println("hello");
            Process process = new ProcessBuilder("python", "--version").start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            int exitCode = process.waitFor();
            System.out.println("\nExited with error code : " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

}

它适用于 Windows(返回我系统的 python 版本),但我的 macbook 中的相同代码返回行尾,所以基本上是空的。¿ 这需要根据操作系统进行进一步配置吗?¿为什么会这样?

标签: javamacosspring-bootterminal

解决方案


你得到什么错误代码?

(至少)有两种解释;该错误代码将指示它是哪一个。

你没有运行 python,或者运行“错误的”python

这意味着您收到某种错误代码或异常。

造成这种情况的可能原因是路径问题。

Runningpython就像那样 - 就像根本没有路径信息一样,名义上必然会被破坏:这不是你的操作系统的工作方式,它不知道如何处理这条路径。

将这样的命令解释为“哦,实际上,遍历$PATH环境变量中列出的每个条目,并将该路径粘贴在该名称前面,看看你是否在那里找到一个可执行文件。如果你这样做了,运行它并停止)。

Java 大多不参与任何 bashisms。但是,在一些奇怪的地方,它确实 - 当你使用 ) 的单字符串版本时,它会尝试进行基本的空间分割new ProcessBuilder,这是一种 shellism,并且它确实尝试进行基本的 PATH 查找,但这就是它结束的地方. 它不会进行*解包,这在 Windows 上是操作系统级别的东西,但在 posix 系统上是一种 shellism。

我强烈建议您避免使用 java 的基本 shellisms。它不可靠且高度特定于操作系统。

所以:总是明确地传递参数(很好,你正在这样做),总是使用ProcessBuilder(很好,你正在这样做),永远不要使用相对路径(那是你出错的地方)。

而是转到错误流

操作系统上的进程通常连接到3 个管道,而不是 2 个。有“标准输入”、“标准输出”和“标准错误”。您自己的 java 进程将这些公开为System.out,inerr.

特别是在 linux 中,将标准从某个进程重定向到一个文件或另一个进程是很常见的。

这意味着标准错误自然具有它倾向于向控制台发出的属性,即使您正在重定向事物。换句话说,术语“标准输出”和“标准错误”在 posix 上是非常愚蠢的名称。更好的命名是“标准流程输出”和“标准流程消息”。

要求 python 打印它的版本有点悬而未决。字符串“Python v3.0.1”或诸如此类的东西当然不是错误,但是如果将其视为“过程的输出”,那就有点可疑了。python 工具的作者很可能认为它更像是“我应该打印给你的一些信息,即使你正在重定向东西。

因此,我的猜测是这个版本正在走向标准错误。

您可以通过两种方式解决此问题:也可以从标准错误中读取,或者使用流程构建器的功能:您可以要求它将标准输出和标准错误捆绑到单个流中(.redirectErrorStream(true))。

如果这个解释是正确的,我希望退出代码为 0。


推荐阅读