C#執行批處理命令

背景

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

直接調用bat文件

我最開始想到的這個方法,最簡單,不須要考慮bat的變量,腳本命令等如@ECHO OFF,至關於雙擊執行了這個腳本文件。可是存在一個問題就是,沒法展現執行進度,因此放棄了。windows

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這個命令;

缺點有三點:3d

  • 批處理的特殊變量沒法識別。如%~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來執行。而且能夠給每一條命令,設置一個單獨的超時時間。code

須要注意的點,從新編輯 bat 批處理文件:blog

  • 刪去批處理獨有的命令,緣由爲沒法變成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); }
}
相關文章
相關標籤/搜索