2011-07-18 22 views
5

は、私はそれがファイルの長さの変化を検出すると、それはテキストを出力追従モードで尾からの出力をキャプチャしようとしています「追跡します」 。何らかの理由で、tail.exeが完全に終了するまでStandardOutput.Read()への呼び出しがブロックされています。キャプチャ標準は

関連するコードサンプル:

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実行の癖ですか? UnxUtilsパッケージの一部としてコンパイルされたバージョン2.0を使用しています。

更新:これは少なくとも部分的にtail.exeのクォークに関連しているようです。私はGnuWin32プロジェクトからCoreUtilsパッケージの一部としてバイナリを取得し、バージョンは5.3.0に上がった。私は再試行せずに従うこと-fオプションを使用する場合、私は(無視しやすい)STDERRの恐ろしい「不正なファイルディスクリプタ」の問題を取得し、プロセスはすぐに終了します。再試行を含めるために-Fオプションを使用すると、不正なファイル記述子メッセージが来て、2回目にファイルを開こうとした後で正常に動作しているようです。

は、おそらく私が試みることができるcoreutils gitリポジトリから、より最近のwin32ビルドありますか?

+0

それはだ場合は、単純に、すべてのX時間単位のファイルをチェックし、「-fテール」あなたはそれを行うことができます

一つの方法は、このようなものです成長し、新しいものを読む。私はC#を知らないが、そこに実装するのは簡単だろうと思う。オープンソース版のtail(GNUなど)のソースコードからインスピレーションを得ることができます。この問題を解決するよりも、C#で必要な機能を実装する方が簡単かもしれません。 – theglauber

+1

FileSystemWatcherまたはTimerを使用して、別の実行可能ファイルを起動する代わりにStreamReaderを使用してクイックシークのストリーム位置を保存するだけでいいですか? – JamieSee

答えて

3

私はそれはあなたが求めているものexatlyはありませんが、ジェームズは、コメントで言うように、あなたが別のプロセスを起動する必要があなたを救うために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を使用していくつかの奇妙なことに気がついたので、タイマーでちょうどチャックされたので、500msごとにファイルをチェックし、新しいものを報告します。 – kmp