ProcessBuilder如何获取进程的输入流、输出流和错误流?

林欢喜 Java经验 发布时间:2025-11-28 06:38:56 阅读数:6204 1
下文笔者讲述Java代码中ProcessBuilder获取进程的输入流、输出流、错误流的方法及示例分享,如下所示

ProcessBuilder简介

`ProcessBuilder` 本身不直接提供输入流、输出流和错误流
    而是通过其启动的 `Process` 对象来获取。

一个进程的标准流包括:
- 标准输入流 (`stdin`):`Process.getOutputStream()`
- 标准输出流 (`stdout`):`Process.getInputStream()`
- 标准错误流 (`stderr`):`Process.getErrorStream()`

1. `Process.getOutputStream()`

- 作用:
      获取一个输出流,
      通过这个流写入的数据会成为目标进程的标准输入(`stdin`)。
- 数据走向:
       当前 Java 程序 → `OutputStream` → 目标进程

    可用它向一个需要交互的命令或程序发送数据
      如 `python` 或 `bc`。

2. `Process.getInputStream()`

- 作用:
     获取一个输入流,这个流的数据来源是目标进程的标准输出(`stdout`)。
- 数据走向:
    目标进程 → `InputStream` → 当前 Java 程序

 如:获取命令执行结果(比如 `ls -l` 的输出)的主要方式 

3. `Process.getErrorStream()`

- 作用:
      获取一个输入流,这个流的数据来源是目标进程的标准错误输出(`stderr`)
- 数据走向:
  目标进程 → `InputStream` → 当前 Java 程序**

当命令执行出错时(比如 `ls nonexistent_file`)
      错误信息会通过这个流输出。
      强烈建议始终处理这个流
      否则当进程产生大量错误输出时,缓冲区可能会被填满,导致进程阻塞。

例:同时处理 `stdout` 和 `stderr`

 
import java.io.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class StreamGobblerExample {

    public static void main(String[] args) {
        // 示例:执行一个会同时产生标准输出和错误输出的命令
        // Windows: "cmd", "/c", "dir && echo This is stderr 1>&2"
        // Linux/macOS: "sh", "-c", "ls -l && echo This is stderr >&2"
        ProcessBuilder pb = new ProcessBuilder("sh", "-c", "ls -l && echo This is stderr >&2");

        try {
            Process process = pb.start();

            // 使用线程池来并发处理 stdout 和 stderr,避免互相阻塞
            ExecutorService executor = Executors.newFixedThreadPool(2);

            // 处理标准输出
            executor.submit(new StreamGobbler(process.getInputStream(), "STDOUT"));

            // 处理标准错误
            executor.submit(new StreamGobbler(process.getErrorStream(), "STDERR"));

            // 等待进程执行完成
            int exitCode = process.waitFor();
            System.out.println("\nProcess exited with code: " + exitCode);

            executor.shutdown();

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 一个辅助类,用于异步读取输入流并打印。
     * 这可以防止因一个流的缓冲区满而导致进程阻塞。
     */
    private static class StreamGobbler implements Runnable {
        private final InputStream inputStream;
        private final String streamType;

        public StreamGobbler(InputStream inputStream, String streamType) {
            this.inputStream = inputStream;
            this.streamType = streamType;
        }

        @Override
        public void run() {
            // 使用 try-with-resources 确保流被正确关闭
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(streamType + ": " + line);
                }
            } catch (IOException e) {
                // 在某些情况下,当进程被销毁时,流可能会提前关闭,这是正常的
                // e.printStackTrace();
            }
        }
    }
}

代码说明

1. `ProcessBuilder` 创建进程:
       启动了一个 `sh` (或 `cmd`) 进程,执行了两个命令:`ls -l` (列出目录) 和 `echo This is stderr >&2` (将一条消息输出到标准错误)。
2.  `StreamGobbler` 类:这是一个关键的辅助类,它实现了 `Runnable` 接口。
      它的作用是在一个单独的线程中持续读取一个 `InputStream` 并打印其内容。
3.  并发处理:通过 `ExecutorService` 启动两个线程,分别处理 `stdout` 和 `stderr`。
      这样做可以避免一个流的处理速度慢而阻塞另一个流。例如,如果 `stderr` 输出很多,而你的代码只在 `stdout` 读取完后才去读 `stderr`,那么进程可能会因为 `stderr` 缓冲区满而卡住。
4.  `process.waitFor()`:主线程会阻塞在这里,直到子进程执行完毕。
5.  资源管理:在 `StreamGobbler` 中,
     我们使用了 `try-with-resources` 语句来自动关闭 `BufferedReader`,这是一种良好的实践。
方法 返回值对应进程的流 数据流向 用途
`process.getOutputStream()` `OutputStream`标准输入 (`stdin`)Java → 外部进程向外部进程发送数据(如输入密码、命令)
`process.getInputStream()` `InputStream` 标准输出 (`stdout`) 外部进程 → Java 读取外部进程的正常执行结果
`process.getErrorStream()` `InputStream` 标准错误 (`stderr`) 外部进程 → Java 读取外部进程的错误信息
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202511/17642841838520.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者