ZetCode

Java ProcessBuilder 类

最后修改时间:2025 年 4 月 13 日

java.lang.ProcessBuilder 类用于创建操作系统进程。 与 Runtime.exec 方法相比,它提供了更多控制,用于使用特定的环境、工作目录和 I/O 重定向来启动进程。

ProcessBuilder 允许您在启动进程之前配置进程属性。 您可以设置命令、参数、环境变量、工作目录以及重定向标准输入/输出/错误流。

ProcessBuilder 基础知识

ProcessBuilder 类用于创建操作系统进程。 每个 ProcessBuilder 实例管理一组进程属性。 start 方法使用这些属性创建一个新的 Process 实例。

public final class ProcessBuilder {
    public ProcessBuilder(List<String> command) {...}
    public ProcessBuilder(String... command) {...}
    public ProcessBuilder command(List<String> command) {...}
    public ProcessBuilder command(String... command) {...}
    public List<String> command() {...}
    public ProcessBuilder directory(File directory) {...}
    public File directory() {...}
    public ProcessBuilder redirectInput(ProcessBuilder.Redirect source) {...}
    public ProcessBuilder redirectOutput(ProcessBuilder.Redirect destination) {...}
    public ProcessBuilder redirectError(ProcessBuilder.Redirect destination) {...}
    public ProcessBuilder.Redirect redirectInput() {...}
    public ProcessBuilder.Redirect redirectOutput() {...}
    public ProcessBuilder.Redirect redirectError() {...}
    public ProcessBuilder inheritIO() {...}
    public Map<String,String> environment() {...}
    public Process start() throws IOException {...}
}

上面的代码展示了 ProcessBuilder 类提供的主要方法。 这些方法允许在启动进程之前对其进行配置。

基本进程执行

此示例演示了使用 ProcessBuilder 执行系统命令的最简单方法。 我们将执行 "echo" 命令来打印一条消息。

BasicProcess.java
package com.zetcode;

import java.io.IOException;

public class BasicProcess {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("echo", "Hello, ProcessBuilder!");
            Process process = pb.start();
            
            int exitCode = process.waitFor();
            System.out.println("Process exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在本例中,我们使用 "echo" 命令及其参数创建一个 ProcessBuilder。 start 方法启动进程,而 waitFor 等待其完成。 请注意,这个简单的例子并没有捕获进程的输出。

读取进程输出

此示例展示了如何捕获和读取进程的输出。 我们将执行 "ls" 命令来列出目录内容并打印输出。

ReadOutput.java
package com.zetcode;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ReadOutput {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("ls", "-l");
            Process process = pb.start();
            
            // Read the output from the process
            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("\nProcess exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里,我们使用 getInputStream 来获取进程的标准输出流。 我们将其包装在 BufferedReader 中以逐行读取输出。 当您需要在 Java 中处理命令输出时,此模式很常见。

设置工作目录

此示例演示了如何为进程设置工作目录。 我们将在特定目录中执行 "ls" 命令。

WorkingDirectory.java
package com.zetcode;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

public class WorkingDirectory {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("ls");
            pb.directory(new File("/tmp"));  // Set working directory
            
            System.out.println("Working directory: " + pb.directory());
            
            Process process = pb.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("\nProcess exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

directory 方法设置进程的工作目录。 在本例中,我们列出了 "/tmp" 目录的内容。 工作目录会影响执行命令使用的相对路径。

环境变量

此示例演示了如何修改进程的环境变量。 我们将添加一个自定义变量并打印环境。

EnvironmentVars.java
package com.zetcode;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;

public class EnvironmentVars {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("printenv");
            
            // Get and modify the environment
            Map<String, String> env = pb.environment();
            env.put("CUSTOM_VAR", "Hello from Java!");
            
            System.out.println("Environment variables:");
            env.forEach((k, v) -> System.out.println(k + "=" + v));
            
            Process process = pb.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("\nProcess exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

environment 方法返回一个可修改的环境变量映射。 我们添加了自定义变量,该变量将可用于子进程。 "printenv" 命令显示所有环境变量。

将输出重定向到文件

此示例演示了如何将进程输出重定向到文件,而不是在 Java 中读取它。 我们将把 "ls" 的输出保存到一个文件中。

RedirectOutput.java
package com.zetcode;

import java.io.File;
import java.io.IOException;

public class RedirectOutput {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("ls", "-l");
            
            // Redirect output to a file
            pb.redirectOutput(new File("output.txt"));
            
            Process process = pb.start();
            int exitCode = process.waitFor();
            
            System.out.println("Process completed. Output saved to output.txt");
            System.out.println("Exit code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

redirectOutput 方法将进程的标准输出定向到文件。 当您想保存命令输出而不使用 Java 处理它时,这很有用。 如果文件不存在,它将被创建;如果文件存在,它将被覆盖。

处理错误流

此示例展示了如何处理标准输出和错误流。 我们将执行一个产生错误输出的命令并捕获这两个流。

ErrorHandling.java
package com.zetcode;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ErrorHandling {
    public static void main(String[] args) {
        try {
            ProcessBuilder pb = new ProcessBuilder("ls", "nonexistent_file");
            
            // Merge error stream with output stream
            pb.redirectErrorStream(true);
            
            Process process = pb.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("\nProcess exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

通过设置 redirectErrorStream(true),我们将错误流与标准输出流合并。 这简化了通过单个 InputStream 读取这两个流的过程。 对于错误情况,退出代码将非零。

运行多个命令

此示例演示了如何按顺序执行多个命令。 我们将运行两个命令并检查它们的状态码。

MultipleCommands.java
package com.zetcode;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class MultipleCommands {
    public static void main(String[] args) {
        List<String> commands = new ArrayList<>();
        commands.add("date");
        commands.add("uname -a");
        
        for (String cmd : commands) {
            try {
                System.out.println("Executing: " + cmd);
                
                ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
                pb.redirectErrorStream(true);
                
                Process process = pb.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("Exit code: " + exitCode + "\n");
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

此示例展示了如何按顺序执行多个命令。 我们将每个命令字符串拆分为 ProcessBuilder 的各个部分。 每个命令的输出和退出代码都会显示。 这种模式对于运行几个系统命令非常有用。

来源

Java ProcessBuilder 类文档

在本文中,我们通过实际示例介绍了 Java ProcessBuilder 类。 ProcessBuilder 提供了用于从 Java 应用程序执行和管理系统进程的强大功能。

作者

我叫 Jan Bodnar,是一名经验丰富的程序员,在这个领域拥有多年的经验。 我于 2007 年开始撰写编程文章,至今已创作了 1,400 多篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程