Java 通過 Runtime.exec() 啟動外部進程,返回 Process 物件管理生命週期。最簡單用法:
// 啟動系統畫圖程式
Process p = Runtime.getRuntime().exec("mspaint");
p.waitFor(); // 等待進程結束
// Windows
Runtime.getRuntime().exec("notepad.exe");
// Linux/Mac
Runtime.getRuntime().exec("/usr/bin/gedit");
// 方式一:單一字串(依賴系統 Shell 解析,跨平台風險大)
Runtime.getRuntime().exec("cmd /c dir C:\\Windows");
// 方式二:字串陣列(推薦,繞過 Shell,參數邊界清晰)
String[] cmd = {"C:\\Program Files\\Microsoft Office\\Office\\WINWORD.EXE", "D:\\doc.docx"};
Runtime.getRuntime().exec(cmd);
// Java 6+ Desktop API:自動調用系統預設程式開啟檔案/URI
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(new File("D:\\report.pdf")); // 開啟 PDF
Desktop.getDesktop().browse(new URI("https://example.com")); // 開啟網頁
Desktop.getDesktop().edit(new File("config.txt")); // 編輯文字檔
}
| 塹坑 | 後果 | 解決方案 |
|---|---|---|
| 忽略輸出/錯誤流 | 緩衝區填滿導致子進程卡死 | 必須在單獨線程中消費 getInputStream()、getErrorStream() |
| 僅用 waitFor() 無超時 | 主線程永久阻塞 | Java 8+ waitFor(timeout, TimeUnit) 或 Future 超時控制 |
| 工作目錄與環境變數未控制 | 路徑解析失敗、環境污染 | 使用 exec(cmd, envp, dir) 重載明確指定 |
| Windows 路徑空格處理 | "Program Files" 導致參數分割錯誤 | 字串陣列模式自動處理,或手動加雙引號 |
ProcessBuilder pb = new ProcessBuilder("python", "script.py", "--input", "data.txt");
pb.directory(new File("/workspace")); // 設置工作目錄
pb.environment().put("PYTHONPATH", "/libs"); // 設置環境變數
pb.redirectErrorStream(true); // 合併錯誤流到標準輸出
pb.redirectOutput(ProcessBuilder.Redirect.to(new File("output.log"))); // 重定向輸出
Process process = pb.start();
// 異步處理輸出
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
reader.lines().forEach(System.out::println);
}
int exitCode = process.waitFor(30, TimeUnit.SECONDS) ? process.exitValue() : -1;
絕對禁止 將未經驗證的用戶輸入直接拼接到命令字串中:
// 危險!用戶輸入 "test.txt; rm -rf /"
Runtime.getRuntime().exec("cat " + userInput);
// 安全:使用字串陣列,參數不會被 Shell 解釋
new ProcessBuilder("cat", userInput).start();
Runtime.exec() 是 Java 1.0 遺留 API,功能受限且易誤用。現代開發請全面遷移至 ProcessBuilder(Java 5+)或 ProcessHandle(Java 9+ 進程管理),並優先考慮 Desktop API 處理檔案關聯開啟需求。任何涉及外部命令執行的代碼,都必須嚴格驗證輸入、妥善處理流、設置超時、記錄審計日誌。