核心方法及代码示例
使用 Runtime.exec()(基础方法)
try {
// 执行命令
Process process = Runtime.getRuntime().exec("ls -l /home");
// 获取命令输出
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);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
注意事项:
- 必须消费输入/错误流,否则可能阻塞进程。
- 使用
waitFor()确保命令执行完成。
使用 ProcessBuilder(推荐方式)
try {
// 构建命令及参数(避免注入风险)
ProcessBuilder pb = new ProcessBuilder("ls", "-l", "/home");
// 重定向错误流到标准输出
pb.redirectErrorStream(true);
// 启动进程
Process process = pb.start();
// 读取输出
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
)) {
reader.lines().forEach(System.out::println);
}
// 检查执行结果
if (process.waitFor() == 0) {
System.out.println("Success!");
} else {
System.out.println("Failed!");
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
优势:
- 参数列表化处理,避免命令注入(如用户输入
rm -rf /)。 - 支持目录设置、环境变量配置等。
第三方库:Apache Commons Exec
适用场景:需超时控制、复杂流处理的场景。
Maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.4.0</version>
</dependency>
示例代码:
CommandLine cmd = new CommandLine("ls");
cmd.addArgument("-l");
cmd.addArgument("/home");
DefaultExecutor executor = new DefaultExecutor();
executor.setExitValue(0); // 设置成功退出码
// 设置超时(单位:毫秒)
ExecuteWatchdog watchdog = new ExecuteWatchdog(5000);
executor.setWatchdog(watchdog);
// 捕获输出
ByteArrayOutputStream output = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(output);
executor.setStreamHandler(streamHandler);
try {
executor.execute(cmd);
System.out.println("Output: " + output.toString());
} catch (ExecuteException e) {
System.err.println("Execution failed: " + e.getMessage());
}
安全性与最佳实践
-
命令注入防御:
- 错误示例:
Runtime.getRuntime().exec("sh -c 'user_input'") - 正确做法:使用
ProcessBuilder拆分参数:// 安全方式 new ProcessBuilder("ls", userInputPath)
- 错误示例:
-
关键操作:
- 始终处理输入流、输出流、错误流,防止阻塞。
- 使用
waitFor()或超时机制(如Commons Exec)避免僵尸进程。 - 检查退出码(0表示成功,非0通常为错误)。
-
权限控制:
- Java进程需有执行命令的权限(如使用
sudo需配置/etc/sudoers)。
- Java进程需有执行命令的权限(如使用
常见问题解决
- 乱码问题:指定流编码格式:
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
- 环境变量问题:通过
ProcessBuilder.environment()传递变量:pb.environment().put("PATH", "/custom/path:" + System.getenv("PATH")); - 超时处理:使用
Commons Exec或线程池:if (!process.waitFor(30, TimeUnit.SECONDS)) { process.destroyForcibly(); // 强制终止 }
方法对比
| 方法 | 适用场景 | 优势 |
|---|---|---|
Runtime.exec() |
简单命令快速调用 | JDK原生支持 |
ProcessBuilder |
需安全参数或环境控制的场景 | 安全性高、灵活性强 |
| Apache Commons Exec | 企业级应用(超时/流管理) | 功能完善、减少样板代码 |
- 简单命令:优先用
ProcessBuilder(安全易用)。 - 复杂需求:选择
Apache Commons Exec(超时/流管理)。 - 避免直接拼接用户输入到命令,严格处理流和退出状态。
引用说明:
本文代码示例基于 Oracle Java 官方文档及 Apache Commons Exec 最佳实践,Linux 命令执行权限相关建议参考《Linux 系统管理手册》(ISBN 978-0596009526),安全规范依据 OWASP Command Injection 防御指南。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/7573.html