核心实现方式
Java通过Runtime或ProcessBuilder类执行外部命令,本质都是启动系统进程。
使用 Runtime.exec()(传统方法)
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class RunDOSCommand {
public static void main(String[] args) {
try {
// 执行命令
Process process = Runtime.getRuntime().exec("cmd /c dir C:\\");
// 读取命令输出
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), "GBK") // 中文系统需指定编码
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令执行完成
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 关键参数说明:
cmd /c [命令]:执行后关闭命令行窗口(/c可替换为/k保持窗口打开)。- 路径中的空格需用双引号包裹(如
"C:\\Program Files")。 - 中文系统必须指定编码(如
GBK),否则输出乱码。
使用 ProcessBuilder(推荐方式)
public class ProcessBuilderExample {
public static void main(String[] args) {
try {
// 构建命令及参数
ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "ping", "www.baidu.com");
// 设置工作目录(可选)
builder.directory(new File("C:\\"));
// 执行并获取输出
Process process = builder.start();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), "GBK")
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 优势:
- 支持参数列表(避免手动拼接命令)。
- 可设置工作目录、环境变量。
- 更安全的命令解析(降低注入风险)。
关键注意事项
-
命令注入风险
禁止直接拼接用户输入到命令中!// 危险示例!用户输入可能执行恶意命令 String userInput = args[0]; Runtime.getRuntime().exec("cmd /c dir " + userInput); // 若输入"& format C:"将导致灾难解决方案:使用
ProcessBuilder拆分参数,或严格校验输入。 -
跨平台兼容性
- Windows命令需以
cmd /c开头(如cmd /c dir)。 - Linux/Mac使用
/bin/sh -c(如/bin/sh -c ls)。 - 建议通过条件判断系统类型:
String os = System.getProperty("os.name").toLowerCase(); if (os.contains("win")) { builder.command("cmd", "/c", "dir"); } else { builder.command("sh", "-c", "ls"); }
- Windows命令需以
-
处理输出流阻塞
进程的输出流和错误流必须被读取,否则可能导致线程阻塞。
优化方案:开独立线程消费流:Thread outputThread = new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { System.out.println("OUT: " + line); } } catch (IOException e) { e.printStackTrace(); } }); outputThread.start(); -
超时控制
避免长时间阻塞:if (!process.waitFor(30, TimeUnit.SECONDS)) { // 设置30秒超时 process.destroy(); // 终止进程 throw new RuntimeException("Command timed out"); }
常见应用场景
- 文件操作
// 复制文件 ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "copy", "src.txt", "dest.txt"); - 网络诊断
// Ping测试 ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "ping", "192.168.1.1"); - 启动外部程序
// 打开记事本 Runtime.getRuntime().exec("notepad.exe");
最佳实践总结
- 安全第一:始终校验外部输入,优先用
ProcessBuilder避免命令注入。 - 编码明确:中文Windows使用
GBK,Linux/Mac用UTF-8。 - 资源释放:关闭
InputStream、OutputStream防止资源泄漏。 - 错误处理:检查
exitCode并读取错误流(process.getErrorStream())。 - 权限管理:需
Administrator权限的命令(如关机),需以管理员身份运行Java进程。
引用说明参考Oracle官方文档 ProcessBuilder 和 Runtime.exec() 的实现规范,并结合了Java安全编程实践。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/7408.html