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

Java 與 C# 執行命令列批次檔並捕獲返回碼的最佳實踐

     編輯:Java 相關知識

場景

應用程式需呼叫外部命令列工具(如資料庫備份 dump、壓縮 7z),並根據執行結果(成功/失敗)進行後續流程控制。關鍵在於:如何正確捕獲進程退出碼,並區分標準輸出與錯誤輸出。

批次檔範本

@echo off
dump -uroot -p1234 --database gbase > 1.sql 2>Nul
if %errorlevel%==0 goto next
goto error

:next
echo OK
goto end

:error
echo ERROR

:end
  • > 1.sql:標準輸出導向檔案。
  • 2>Nul:錯誤輸出丟棄(Windows 空裝置)。
  • %errorlevel%:上一條命令的退出碼,0 表成功。
  • 透過 echo OK/ERROR 讓呼叫端從標準輸出讀取狀態字串。

Java 實現

public class BatchExecutor {
    public static void main(String[] args) throws Exception {
        ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "backup.bat");
        pb.redirectErrorStream(true); // 合併 stderr 到 stdout,簡化讀取
        Process process = pb.start();

        // 非同步讀取輸出,避免緩衝區塞滿導致死鎖
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream(), "MS950"))) { // 中文編碼
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println("[STDOUT] " + line);
                if (line.contains("OK")) {
                    System.out.println(">> 批次檔執行成功");
                } else if (line.contains("ERROR")) {
                    System.out.println(">> 批次檔執行失敗");
                }
            }
        }

        int exitCode = process.waitFor();
        System.out.println("進程退出碼: " + exitCode);
    }
}

C# 實現

using System.Diagnostics;

public class BatchRunner {
    public static void Run() {
        var startInfo = new ProcessStartInfo {
            FileName = "cmd.exe",
            Arguments = "/c backup.bat",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            StandardOutputEncoding = Encoding.GetEncoding(950), // Big5
            StandardErrorEncoding = Encoding.GetEncoding(950)
        };

        using (var process = Process.Start(startInfo)) {
            // 非同步讀取雙流
            var outputTask = process.StandardOutput.ReadToEndAsync();
            var errorTask = process.StandardError.ReadToEndAsync();

            process.WaitForExit();

            string output = outputTask.Result;
            string error = errorTask.Result;

            Console.WriteLine("[STDOUT]\n" + output);
            if (!string.IsNullOrEmpty(error))
                Console.WriteLine("[STDERR]\n" + error);

            Console.WriteLine($"進程退出碼: {process.ExitCode}");
            if (output.Contains("OK")) Console.WriteLine(">> 成功");
            else if (output.Contains("ERROR")) Console.WriteLine(">> 失敗");
        }
    }
}

進階技巧與避坑指南

問題解決方案
輸出緩衝區塞滿導致進程卡死Java: 獨立執行緒讀取 InputStream/ErrorStream;C#: ReadToEndAsync 或事件式 OutputDataReceived
中文亂碼指定編碼:Java MS950/CP950,C# Encoding.GetEncoding(950)
需區分 stdout/stderrJava: ProcessBuilder.redirectErrorStream(false) 分別讀取;C#: 同時開啟 RedirectStandardOutput/Error
批次檔內多條命令,僅最後一條的 errorlevel 有效每條關鍵命令後立即 if %errorlevel% neq 0 exit /b %errorlevel% 向外部傳遞
需取得批次檔內部變數值批次檔 echo ::RESULT::%VAR%,呼叫端解析特定前綴行

總結

核心原則:非同步讀取雙流 + 指定編碼 + 解析約定輸出標記。Java 建議使用 ProcessBuilder 替代舊版 Runtime.exec;C# 則善用 ProcessStartInfo 細粒度控制。若批次邏輯複雜,建議改寫為 Python/PowerShell 腳本,再由 Java/C# 呼叫,維護性更佳。

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