首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Java 進程流操作:循環中重複關閉 OutputStream 導致 EBADF 異常

     編輯:程式設計疑難解答

異常現場

public void execShellCmd(String cmd) {
    try {
        OutputStream output = process.getOutputStream();
        DataOutputStream dataOutput = new DataOutputStream(output);
        dataOutput.writeBytes(cmd);
        dataOutput.flush();
        output.close();        // 關鍵:關閉了進程的標準輸入流!
        dataOutput.close();
    } catch (Throwable t) { t.printStackTrace(); }
}

while (true) {
    execShellCmd(str); // 第二次循環時,流已關閉,寫入失敗
    sleep(time);
}

異常:java.io.IOException: write failed: EBADF (Bad file number)

根因

首次調用 execShellCmd 結束時,output.close() 關閉了 Process 對象的標準輸入流(`stdin`)。 該流屬於進程級資源,**關閉後不可重新打開**,第二次循環寫入即報 `EBADF` (Bad file descriptor)。

正確模式:流的生命周期與進程綁定

// 1. 啟動進程時一次性獲取流,作為類成員持有
Process process = Runtime.getRuntime().exec("sh"); // 或 "cmd.exe"
OutputStream stdin = process.getOutputStream();
DataOutputStream dos = new DataOutputStream(stdin);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

// 2. 循環中僅寫入、刷新,不關閉
public void sendCommand(String cmd) {
    try {
        dos.writeBytes(cmd + "\n"); // 別忘換行符,shell 按行讀取
        dos.flush();
    } catch (IOException e) { /* 處理斷管 */ }
}

// 3. 程序退出/銷毀時統一關閉
public void shutdown() {
    try { dos.close(); } catch (IOException ignored) {}
    try { reader.close(); } catch (IOException ignored) {}
    process.destroy();
}

補充避坑指南

  • 命令結尾必須帶換行符 (`\n`),否則 shell 陰塞在讀取行。
  • 同步讀取 stdout/stderr:另開線程持續讀取 process.getInputStream() 和 getErrorStream(),否則管道滿導致進程阻塞。
  • Java 9+ 使用 ProcessBuilder:new ProcessBuilder("sh").redirectErrorStream(true).start() 合併錯誤流,簡化讀取。
  • 超時控制:process.waitFor(timeout, TimeUnit.SECONDS) 避免死等。

**核心原則:進程的標準流是長連接,隨進程生命周期開啟/關閉,不可在單次命令中反覆開關。**

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