ProcessBuilder 启动外部进程
ProcessBuilder 是 Java 中用于创建操作系统进程的类,相比旧版的 Runtime.exec(),它提供了更灵活和强大的控制能力。

基本用法
创建并启动简单进程
import java.io.*;
public class ProcessBuilderExample {
public static void main(String[] args) throws IOException {
// 创建 ProcessBuilder 实例
ProcessBuilder pb = new ProcessBuilder("ls", "-la");
// 启动进程
Process process = pb.start();
// 读取输出
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
核心功能
设置工作目录
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dir");
// 设置工作目录
pb.directory(new File("C:/Users/YourName"));
环境变量管理
ProcessBuilder pb = new ProcessBuilder("printenv");
Map<String, String> env = pb.environment();
// 添加/修改环境变量
env.put("MY_VAR", "custom_value");
env.put("PATH", env.get("PATH") + ":/custom/path");
// 清除所有环境变量
env.clear();
重定向输入输出
ProcessBuilder pb = new ProcessBuilder("grep", "error");
// 从文件读取输入
pb.redirectInput(new File("input.log"));
// 输出到文件
pb.redirectOutput(new File("output.txt"));
// 错误输出合并到标准输出
pb.redirectErrorStream(true);
// 或者单独重定向错误输出
pb.redirectError(new File("error.log"));
完整示例
执行复杂命令并处理输出
import java.io.*;
import java.util.concurrent.TimeUnit;
public class ProcessBuilderDemo {
public static void main(String[] args) {
try {
// 创建命令列表(Windows 示例)
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe",
"/c",
"ping -n 3 8.8.8.8"
);
// 配置进程
pb.directory(new File("C:/temp"));
pb.redirectErrorStream(true);
// 启动进程
Process process = pb.start();
// 读取输出
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("OUTPUT: " + line);
}
}
// 等待进程完成(带超时)
boolean finished = process.waitFor(30, TimeUnit.SECONDS);
if (finished) {
int exitCode = process.exitValue();
System.out.println("进程退出码: " + exitCode);
} else {
System.out.println("进程超时,强制终止");
process.destroyForcibly();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
高级用法
使用管道(Pipe)
import java.util.List;
public class PipeExample {
public static void main(String[] args) throws IOException {
// 创建两个进程
ProcessBuilder pb1 = new ProcessBuilder("ls", "-la");
ProcessBuilder pb2 = new ProcessBuilder("grep", "java");
// 启动第一个进程
Process process1 = pb1.start();
// 将第一个进程的输出作为第二个进程的输入
Process process2 = pb2.start();
// 连接管道
try (InputStream is = process1.getInputStream();
OutputStream os = process2.getOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
}
// 读取最终结果
BufferedReader reader = new BufferedReader(
new InputStreamReader(process2.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
常见平台命令示例
public class PlatformSpecific {
// Linux/Mac 命令
public static void unixCommands() throws IOException {
// 列出目录
new ProcessBuilder("ls", "-la", "/tmp").start();
// 执行脚本
new ProcessBuilder("bash", "-c", "echo 'Hello'").start();
// 查看进程
new ProcessBuilder("ps", "-ef").start();
}
// Windows 命令
public static void windowsCommands() throws IOException {
// 列出目录
new ProcessBuilder("cmd", "/c", "dir").start();
// 查看进程
new ProcessBuilder("tasklist").start();
// IP 配置
new ProcessBuilder("ipconfig", "/all").start();
}
}
错误处理最佳实践
public class RobustProcessBuilder {
public static void executeCommand(List<String> command) {
Process process = null;
try {
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
process = pb.start();
// 启动读取线程防止缓冲阻塞
Thread outputReader = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
outputReader.start();
// 等待完成
if (!process.waitFor(10, TimeUnit.SECONDS)) {
process.destroyForcibly();
throw new RuntimeException("进程超时");
}
int exitCode = process.exitValue();
if (exitCode != 0) {
throw new RuntimeException("进程返回非零退出码: " + exitCode);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException("执行命令失败: " + command, e);
} finally {
if (process != null) {
process.destroy();
}
}
}
}
关键注意事项
- 线程安全: Process 不是线程安全的,要考虑并发访问
- 资源释放: 始终关闭 InputStream/OutputStream
- 阻塞问题: 大输出可能导致进程阻塞,使用独立线程读取
- 跨平台: 注意命令在不同操作系统上的差异
- 安全: 避免命令注入,使用参数列表而非字符串拼接
ProcessBuilder 为 Java 提供了强大且可靠的外部进程管理能力,合理使用可以很好地扩展应用的功能边界。