核心方法:Runtime.exec() 和 ProcessBuilder
Java通过Runtime.getRuntime().exec()或更灵活的ProcessBuilder类启动CMD进程,二者本质相同,但ProcessBuilder提供更精细的控制(如工作目录、环境变量)。
方法1:使用 Runtime.exec()(简单命令)
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class RunCmd {
public static void main(String[] args) {
try {
// 执行命令:打开CMD并运行 dir(Windows)或 ls(Linux/Mac)
String os = System.getProperty("os.name").toLowerCase();
String command = os.contains("win") ? "cmd /c dir" : "/bin/bash -c ls";
Process process = Runtime.getRuntime().exec(command);
// 读取命令输出
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // 打印CMD输出
}
// 等待命令执行完成(阻塞当前线程)
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 关键参数说明:
cmd /c [命令]:Windows中执行命令后关闭CMD窗口。cmd /k [命令]:执行命令后保持CMD窗口打开(需手动关闭)。- Linux/Mac使用
/bin/bash -c [命令]。
方法2:使用 ProcessBuilder(推荐:复杂场景)
public class RunCmdAdvanced {
public static void main(String[] args) {
try {
// 1. 定义命令(支持参数)
ProcessBuilder builder = new ProcessBuilder();
if (System.getProperty("os.name").toLowerCase().contains("win")) {
builder.command("cmd.exe", "/c", "ping", "google.com");
} else {
builder.command("bash", "-c", "ping -c 4 google.com");
}
// 2. 设置工作目录(默认项目根目录)
builder.directory(new File("C:\\myfolder")); // 指定路径
// 3. 合并错误流到标准输出(避免阻塞)
builder.redirectErrorStream(true);
// 4. 启动进程并处理输出
Process process = builder.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))
) {
reader.lines().forEach(System.out::println);
}
// 5. 检查执行结果
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
必知注意事项
-
路径与空格处理:
- 路径含空格时需用双引号包裹:
cmd /c "C:\\Program Files\\tool.exe"。 - 使用
ProcessBuilder可避免手动拼接命令,降低错误率。
- 路径含空格时需用双引号包裹:
-
流处理(防死锁):
- 必须读取输入流(
getInputStream())和错误流(getErrorStream()),否则缓冲区满会导致进程阻塞。 - 简单方案:用
builder.redirectErrorStream(true)合并错误流到标准输出。
- 必须读取输入流(
-
异步执行:
- 长时间任务需异步处理,避免阻塞主线程:
new Thread(() -> { try { Process process = builder.start(); // ... 处理输出 } catch (IOException e) { e.printStackTrace(); } }).start();
- 长时间任务需异步处理,避免阻塞主线程:
-
权限问题:
需Java进程有系统权限(如管理员/root),无权限时命令可能失败。
-
跨平台兼容:
- 通过
System.getProperty("os.name")判断操作系统,动态切换命令。
- 通过
安全风险与防御
- 命令注入漏洞:
禁止直接拼接用户输入到命令中!
错误示例:Runtime.getRuntime().exec("cmd /c delete " + userInput);
修复方案:
使用参数化传递(ProcessBuilder自动处理):// 安全方式:参数作为独立字符串传递 ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "safe-command", userInput);
常见问题解决
-
Q:命令执行无输出?
A:检查是否未读取输入流,或命令本身无输出(如start启动新窗口)。 -
Q:中文乱码?
A:指定CMD编码为GBK(Windows默认):BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream(), "GBK") ); -
Q:如何执行多命令?
A:用&&连接或写入批处理文件:ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "cd C:\\ && dir");
- 简单命令:用
Runtime.exec()快速实现。 - 复杂场景:优先选
ProcessBuilder(路径/参数/环境变量可控)。 - 核心原则:
- 始终处理输入/错误流,
- 校验用户输入防注入,
- 考虑跨平台兼容性。
引用说明:
- Oracle官方文档:ProcessBuilder
- Java安全编码标准(SEI CERT)IDS07-J. 防止命令注入
- 跨平台实践参考:Apache Commons Exec(第三方库备选方案)
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/7120.html