核心方法: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