Java 調用系統命令看似簡單,實則暗藏坑點:輸出流/錯誤流未及時讀取會導致進程阻塞、甚至死鎖。本文提供生產級封裝方案。
public class StreamGobbler extends Thread {
private final InputStream is;
private final String type; // "Output" 或 "Error"
public StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
@Override
public void run() {
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(type + " > " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void executeShell(String cmd) {
// 必須用 /bin/sh -c 包裹,支持管道、重定向等 Shell 特性
String[] cmds = {"/bin/sh", "-c", cmd};
try {
Process process = Runtime.getRuntime().exec(cmds);
// 關鍵:啟動兩個線程分別消費 stdout / stderr
new StreamGobbler(process.getInputStream(), "Output").start();
new StreamGobbler(process.getErrorStream(), "Error").start();
// 等待進程結束
int exitCode = process.waitFor();
System.out.println("命令退出碼:" + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
.sh 腳本,Java 僅調用腳本,降低耦合與轉義麻煩| 現象 | 原因 | 解決 |
|---|---|---|
| 程序卡住不返回 | 未讀取流,緩衝區填滿 | 雙線程消費流 |
| 含管道/重定向的命令失效 | Runtime.exec 不解析 Shell 語法 | 用 /bin/sh -c "cmd" 包裹 |
| 中文亂碼 | 編碼不匹配 | 指定 InputStreamReader(is, "UTF-8") |
public class LinuxCmdExecutor {
public static void main(String[] args) {
new LinuxCmdExecutor().executeShell("ls -l /tmp | grep test");
}
// 粘貼上方 executeShell 與 StreamGobbler 代碼
}
掌握流處理本質,Java 執行系統命令不再是「玄學」。