2011-07-18 32 views
5

我試圖在尾隨模式下捕獲尾部輸出,它在其中輸出文本,因爲它檢測文件長度的變化 - 對於添加行後的日誌文件特別有用。出於某種原因,我對StandardOutput.Read()的調用被阻塞,直到tail.exe完全退出。從尾部捕獲標準-f「follow」

相關的代碼示例:

var p = new Process() { 
    StartInfo = new ProcessStartInfo("tail.exe") { 
    UseShellExecute = false, 
    RedirectStandardOutput = true, 
    Arguments = "-f c:\\test.log" 
    } 
}; 
p.Start(); 

// the following thread blocks until the process exits 
Task.Factory.StartNew(() => p.StandardOutput.Read()); 
// main thread wait until child process exits 
p.WaitForExit(); 

我一直在使用:對於具有相同的阻斷行爲OutputDataReceived事件處理程序的支持,也嘗試:

p.OutputDataReceived += (proc, data) => { 
    if (data != null && data.Data != null) { 
    Console.WriteLine(data.Data); 
    } 
}; 
p.BeginOutputReadLine(); 

我身邊有一點點代碼對StandardOutput.Read()的調用,但是這會簡化該示例,並且仍會出現不希望的阻止行爲。是否還有其他事情可以讓我的代碼在子應用程序退出之前響應StandardOutput流中的數據可用性?

這僅僅是tail.exe運行的一個怪癖嗎?我正在使用版本2.0作爲UnxUtils包的一部分進行編譯。

更新:這看起來似乎至少部分與tail.exe中的怪癖有關。作爲CoreUtils軟件包的一部分,我從GnuWin32項目中獲得了二進制文件,版本高達5.3.0。如果我使用-f選項來關閉而不重試,我會在STDERR上遇到可怕的「壞文件描述符」問題(容易忽略),並且該進程立即終止。如果我使用-F選項來包含重試,在壞文件描述符消息到來後它似乎能夠正常工作,並且它會嘗試第二次打開該文件。

是否有可能從coreutils git倉庫更新的win32版本我可以試試嗎?

+0

「尾-f」簡單地檢查每x時間單位的文件,如果它是成長,閱讀新的東西。我不知道c#,但我想在那裏很容易實現。您可以從源代碼中獲得開源版本的尾巴(如GNU)的靈感。在C#中實現你需要的功能可能比解決這個問題更容易。 – theglauber

+1

您是否考慮過使用FileSystemWatcher或Timer並存儲流位置以便使用StreamReader進行快速搜索而不是啓動另一個可執行文件? – JamieSee

回答

3

我知道這不是你問的問題,但正如James在評論中所說的那樣,你可以直接在C#中執行相同的功能,以免你需要啓動另一個進程。你可以做到這一點

一種方法是這樣的:

using System; 
using System.IO; 
using System.Text; 
using System.Threading; 

public class FollowingTail : IDisposable 
{ 
    private readonly Stream _fileStream; 
    private readonly Timer _timer; 

    public FollowingTail(FileInfo file, 
         Encoding encoding, 
         Action<string> fileChanged) 
    { 

     _fileStream = new FileStream(file.FullName, 
            FileMode.Open, 
            FileAccess.Read, 
            FileShare.ReadWrite); 

     _timer = new Timer(o => CheckForUpdate(encoding, fileChanged), 
          null, 
          0, 
          500); 
    } 

    private void CheckForUpdate(Encoding encoding, 
           Action<string> fileChanged) 
    { 
     // Read the tail of the file off 
     var tail = new StringBuilder(); 
     int read; 
     var b = new byte[1024]; 
     while ((read = _fileStream.Read(b, 0, b.Length)) > 0) 
     { 
      tail.Append(encoding.GetString(b, 0, read)); 
     } 

     // If we have anything notify the fileChanged callback 
     // If we do not, make sure we are at the end 
     if (tail.Length > 0) 
     { 
      fileChanged(tail.ToString()); 
     } 
     else 
     { 
      _fileStream.Seek(0, SeekOrigin.End); 
     } 
    } 

    // Not the best implementation if IDisposable but you get the idea 
    // See http://msdn.microsoft.com/en-us/library/ms244737(v=vs.80).aspx 
    // for how to do it properly 
    public void Dispose() 
    { 
     _timer.Dispose(); 
     _fileStream.Dispose(); 
    } 
} 

然後調用例如:

new FollowingTail(new FileInfo(@"C:\test.log"), 
        Encoding.ASCII, 
        s => 
        { 
         // Do something with the new stuff here, e.g. print it 
         Console.Write(s); 
        }); 
+0

感謝您的回答 - 您的代碼非常好。我實際上嘗試過類似的東西,但是這對於當前正在使用的文件不起作用。 tail.exe仍然會讀取鎖定的文件,而FileStream不會 - 至少不能使用我已經能夠確定的選項。 – Goyuix

+1

我想我只是解決了鎖定問題 - 您需要指定FileShare.ReadWrite,這意味着允許其他進程讀寫訪問(http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx) 。我已經更新了這個改變,並且我注意到了一些使用FileSystemWatcher的怪異特性,所以只需將它放在一個計時器中,以便每隔500毫秒檢查一次文件並報告任何新事物。 – kmp