.Net控制Git.exe進程交互遇到的問題?
背景:題主想把Git集成到開發框架中的一個工作流中,做類似持續集成的功能。
描述:在使用.Net的Process類調用git clone(代碼在後面),發現StandardOutput輸出為"",而StandardError反而得了"Cloning into "BunnierPythonStudy"...",也就是直接使用cmd調用本命令的第一行(見下圖),然而後面幾行輸出都獲取不到,想請教下各位大神,該如何才能獲取到和手動敲cmd一樣的輸出?核心代碼如下: /// &/// & /// 按參數執行指定的進程。
/// &執行程序的路徑。&
/// &傳遞的參數。&
/// &進程的工作目錄。&
/// &標準輸出的結果& public static string Exec(string fileName, string arguments, string workingDirectory = null) { var processStartInfo = new ProcessStartInfo() {FileName = fileName,
Arguments = arguments ?? "", UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true }; if (!string.IsNullOrWhiteSpace(workingDirectory)) {processStartInfo.WorkingDirectory = workingDirectory;
} string resu< using (var process = new Process { StartInfo = processStartInfo }) { process.Start(); result = process.StandardOutput.ReadToEnd();//獲取標準輸出流。 var errResult = process.StandardError.ReadToEnd();//獲取錯誤輸出流。 process.WaitForExit();//等待程序退出(這句放到獲取標準輸出流前也無法獲得,msdn上介紹應該放在標準輸出後防止死鎖)。 if (!string.IsNullOrWhiteSpace(errResult)){
throw new Exception(errResult); } } return resu< }調用代碼如下: //gitPath為操作系統git.exe文件路徑, gitRepositoryUrl為git庫地址,gitWorkFolder為工作目錄 ProcessHelper.Exec(gitPath,string.Format("clone {0}", gitRepositoryUrl), gitWorkFolder)/************************補充****************************///非同步的方式也試過如下,得到的結果和同步方法是一樣的
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =&> { result += e.Data; }; process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =&> { errResult += e.Data; }; process.EnableRaisingEvents = true;process.Start();
process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit();
git-clone(1)
題主在standard error裡面看見消息是對的。我也是第一次見到這種奇怪的做法,怪git的產品狗腦子跟我們不一樣(逃
鑒於這樣,題主可能需要閱讀git文檔的所有文字,扣除裡面的細節,然後做成一個.net library,給大家貢獻一下
--progress Progress status is reported on the standard error stream by default when it is attached to a terminal, unless -q is specified. This flag forces progress status even if the standard error stream is not directed to a terminal.
可以用libgit2sharp啊
https://github.com/libgit2/libgit2sharp你其實不用去重定向,作為 git進程的爸爸, 你這個 進程的控制台的輸入輸出會被 git 繼承,你其實不用這麼麻煩,只需要啟動進程後運行 process.WaitForExit(); 就行了.
默認情況下,git 會使用 isatty 檢測是否是終端,然後決定是否輸出進度,當你重定向 io 後, 實際上是寫入到管道了,所以進度不會輸出,當使用 --progress 時,進度是強制的了,就不關心 isatty.
在 Windows 中是 控制台,也就是字元設備,或者是 mintty 之類的終端模擬器, linux 是 tty.
下面是 .Net Core 實現的.using System;
using System.Diagnostics;
namespace GitClone
{
public class App
{
public static void Main(string[] args)
{
//
Process process = new Process();
process.StartInfo.FileName = "git";
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.Arguments = "clone git@git.oschina.net:bunnier/BunnierPythonStudy.git --progress ";
if (!process.Start())
{
Console.Error.WriteLine("Create Process failed !");
return;
}
process.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
process.StandardError.BaseStream.CopyToAsync(Console.OpenStandardError());
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.Error.WriteLine($"git clone exit: {process.ExitCode}");
}
}
}
}
為什麼進度條在 standard error 輸出呢?首先,一個經典的 UNIX 終端進度條一般是這麼做的:
fprintf(stderr, "%d/%d
", current, total);
// 也有輸出的 "
%d/%d" ,效果差不多但細節略有區別,讀者可以自己思考。
比如一開始先輸出 0/100
,這時候終端內容是
0/100
-
注意0下面的減號表示游標的位置,由於終端接收了
之後游標移動到行首。
的時候就會把 0/100 蓋掉,並且游標又回到行首。如此反覆,就會看到進度在跑。
然而 C 標準庫默認行為是對 stdout 的行為做行緩衝,stderr 不緩衝。
也就是說,僅當 stdout 的 buffer 滿了,或者出現了,buffer 的內容就才會被 flush,你才能在終端上看到。而 stderr 不緩衝,寫入任何字元都會立刻在終端看到。
推薦閱讀:
※為什麼我在git上提交代碼都會重複提交別人更新的代碼?
※Git常用命令
※【Trac】瀏覽器中查看源碼庫
※如何理解git的快照?