首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 執行 Linux 命令最佳實踐:Runtime.exec 與流處理全解析

     編輯:Java 相關知識

核心痛點

Java 調用系統命令看似簡單,實則暗藏坑點:輸出流/錯誤流未及時讀取會導致進程阻塞、甚至死鎖。本文提供生產級封裝方案。

標準實現模式:StreamGobbler 雙線程消費流

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();
    }
}

三大黃金法則

  1. 先讀錯誤流,再讀輸出流 — 避免緩衝區滿導致死鎖
  2. 在 waitFor() 之前啟動消費線程 — 否則進程可能因緩衝區填滿而永久卡住
  3. 複雜命令寫成腳本文件 — 將多條命令、邏輯判斷放入 .sh 腳本,Java 僅調用腳本,降低耦合與轉義麻煩

常見踩坑場景

現象原因解決
程序卡住不返回未讀取流,緩衝區填滿雙線程消費流
含管道/重定向的命令失效Runtime.exec 不解析 Shell 語法用 /bin/sh -c "cmd" 包裹
中文亂碼編碼不匹配指定 InputStreamReader(is, "UTF-8")

進階替代方案

  • ProcessBuilder:更靈活的環境變量、工作目錄控制
  • Apache Commons Exec:提供超時控制、結果收集器等高級功能
  • SSH 庫:遠程執行命令首選 JSch、SSHJ

完整可運行 Demo

public class LinuxCmdExecutor {
    public static void main(String[] args) {
        new LinuxCmdExecutor().executeShell("ls -l /tmp | grep test");
    }
    // 粘貼上方 executeShell 與 StreamGobbler 代碼
}

掌握流處理本質,Java 執行系統命令不再是「玄學」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved