標籤:

.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下面的減號表示游標的位置,由於終端接收了
之後游標移動到行首。

下次輸出 1/100
的時候就會把 0/100 蓋掉,並且游標又回到行首。如此反覆,就會看到進度在跑。

然而 C 標準庫默認行為是對 stdout 的行為做行緩衝,stderr 不緩衝。

也就是說,僅當 stdout 的 buffer 滿了,或者出現了
,buffer 的內容就才會被 flush,你才能在終端上看到。而 stderr 不緩衝,寫入任何字元都會立刻在終端看到。


推薦閱讀:

為什麼我在git上提交代碼都會重複提交別人更新的代碼?
Git常用命令
【Trac】瀏覽器中查看源碼庫
如何理解git的快照?

TAG:Git | NET | C# |