C#執行批處理命令


背景

前段時間,遇到一個需求,需要解壓文件,並且執行里面的 bat 文件。還需要獲取執行進度,並且在錯誤的時候,中斷執行。在這期間,在網上查找了許多的實例,不斷地嘗試,兜兜轉轉的繞了一大圈,記錄一下走過的一些坑。

直接調用bat文件

我最開始想到的這個方法,最簡單,不需要考慮bat的變量,腳本命令等如@ECHO OFF,相當於雙擊執行了這個腳本文件。但是存在一個問題就是,無法展示執行進度,所以放棄了。

using (Process myPro = new Process())
{
    myPro.StartInfo.FileName = Path.Combine(dirPath, batFilePath);
    myPro.StartInfo.UseShellExecute = false;
    myPro.StartInfo.CreateNoWindow = true;
    myPro.Start();
    myPro.WaitForExit();
}

用 cmd.exe 逐行執行命令

相當於打開了一個cmd.exe的窗口,然后在這個窗體里,一行一行的輸入進去命令執行。如下圖:
cmd執行命令

這樣有的好處就是,bat文件不用大修改:

  • 里面的臨時變量,不用重新替換賦值;如上圖的哪個Bslot_images_path變量,在腳本文件中大量使用;
  • 不用刪除 windows 命令,如ping -n 6 127.0.0.1這個命令;

缺點有三點:

  • 批處理的特殊變量無法識別。如%~dp0只可以用在批處理文件中,它是由它所在的批處理文件的目錄位置決定的,是批處理文件所在的盤符:+路徑。
  • 超時設置的問題,如果設置超時,只是針對整個批處理文件,但是每個批處理文件大小不一,並且命令預期執行時長也不盡相同。我想到的最好的辦法就是,針對每條不同的命令,設置不同的超時時長。如果不設置超時,則會出現假死想象,下圖。
  • 執行結果的獲取,無法做到實時的獲取每條命令的返回結果,並做出判斷。只能在退出后&exit,才能獲取。否則執行p.StandardOutput.ReadToEnd();會出現假死狀況。waiting for any device等情況,只能ctrl+c強制退出。

cmd 假死情況

下面是網上找的一個簡單的演示版本,關鍵就是循環輸入處,無法實時的獲得執行的結果;另外就是超時時間問題。

static void Main(string[] args)
{
    Console.WriteLine("請輸入要執行的命令:");
    string strInput = Console.ReadLine();
    Process p = new Process();
    p.StartInfo.FileName = "cmd.exe";  //設置要啟動的應用程序
    p.StartInfo.UseShellExecute = false;  //是否使用操作系統shell啟動
    p.StartInfo.RedirectStandardInput = true;  // 接受來自調用程序的輸入信息
    p.StartInfo.RedirectStandardOutput = true;  //輸出信息
    p.StartInfo.RedirectStandardError = true;  // 輸出錯誤
    p.StartInfo.CreateNoWindow = true;  //不顯示程序窗口
    p.Start();  //啟動程序

    p.StandardInput.WriteLine(strInput+"&exit");  //向cmd窗口發送輸入信息,如果批處理,需要這里做循環輸入
    p.StandardInput.AutoFlush=true;

    string strOuput = p.StandardOutput.ReadToEnd();  //獲取輸出信息
    p.WaitForExit(60 * 1000);  //等待程序執行完退出進程,cmd.exe超時時間
    p.Close();

    Console.WriteLine(strOuput);
    Console.ReadKey();
}

解析命令,執行命令

這個咋一看起來和用 cmd.exe 逐行執行命令很像,這個不同點就是 把每個命令的exe文件單獨拿出來執行,而不是使用cmd.exe來執行。並且可以給每一條命令,設置一個單獨的超時時間。

需要注意的點,重新編輯 bat 批處理文件:

  • 刪去批處理獨有的命令,原因為無法變成exe執行(如:"@SET BASEPATH=%~dp0","@ECHO OFF"等);
  • 刪去windows系統命令,原因為工作目錄非系統PATH,無法找到系統exe(如:"ping -n 6 127.0.0.1","cd A_Debug"等);
  • 替換臨時變量為具體值,目的是為了,變成可以單獨一條拿出來執行的命令(如:"fastboot flash boot0 "A_Debug/boot0.img" ");
  • 刪除空白行以及等待用戶操作的命令(如:"pause > nul")

命令需要拆分:exe執行程序參數超時時長三部分;如:“fastboot flash boot0 "A_Debug/boot0.img" ” 拆分為:

  • exe執行程序“fastboot”;
  • 參數“flash boot0 "A_Debug/boot0.img"”;
  • 超時時長“60000”。

然后把拆分后的參數,傳入執行,具體執行命令的代碼如下。

private List<string> Shell(string exeFile, string command, int timeout, string workingDir, out int exitCode)
{
    List<string> response = new List<string>();
    List<string> output = new List<string>();
    List<string> error = new List<string>();
    Process process = new Process();

    process.StartInfo.FileName = exeFile; //設置要啟動的應用程序,如:fastboot
    process.StartInfo.Arguments = command; // 設置應用程序參數,如: flash boot0 "A_Debug/boot0.img"

    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardInput = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.CreateNoWindow = true;
    process.EnableRaisingEvents = true;  // 獲取或設置在進程終止時是否應激發 Exited 事件;不論是正常退出還是異常退出。
    process.StartInfo.WorkingDirectory = workingDir; // **重點**,工作目錄,必須是 bat 批處理文件所在的目錄
    process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Redirected(output, sender, e);
    process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Redirected(error, sender, e);
    process.Start();
    process.BeginOutputReadLine();  // 開啟異步讀取輸出操作
    process.BeginErrorReadLine();  // 開啟異步讀取錯誤操作


    bool exited = process.WaitForExit(timeout);
    if (!exited)
    {
        process.Kill();  // 通過超時判斷是否執行失敗,極可能為假死狀態。
        // 記錄日志
        response.Add("Error: timed out");
    }

    response.AddRange(output);
    response.AddRange(error);
    exitCode = process.ExitCode; // 0 為正常退出。
    return response;
}

private void Redirected(List<string> dataList, object sender, DataReceivedEventArgs e)
{
    if (e.Data != null){ dataList.Add(e.Data); }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM