从尾部捕获标准 -f“follow”

发布于 2024-11-25 02:11:09 字数 1423 浏览 0 评论 0原文

我正在尝试以跟踪模式捕获 tail 的输出,它在检测到文件长度变化时输出文本 - 对于添加行时跟踪日志文件特别有用。由于某种原因,我对 StandardOutput.Read() 的调用被阻塞,直到 tail.exe 完全退出。

相关代码示例:

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 选项来包含重试,那么在收到错误的文件描述符消息并尝试再次打开文件后,它似乎可以正常工作。

我可以尝试从 coreutils git 存储库中获取更新的 win32 版本吗?

I am trying to capture the output from tail in follow mode, where it outputs the text as it detects changes in the file length - particularly useful for following log files as lines are added. For some reason, my call to StandardOutput.Read() is blocking until tail.exe exits completely.

Relevant code sample:

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();

I have also tried using the support for the OutputDataReceived event handler which exhibits the same blocking behavior:

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

I do have a little bit more code around the call to StandardOutput.Read(), but this simplifies the example and still exhibits the undesirable blocking behavior. Is there something else I can do to allow my code to react to the availability of data in the StandardOutput stream prior to the child application exiting?

Is this just perhaps a quirk of how tail.exe runs? I am using version 2.0 compiled as part of the UnxUtils package.

Update: this does appear to be at least partially related to quirks in tail.exe. I grabbed the binary from the GnuWin32 project as part of the CoreUtils package and the version bumped up to 5.3.0. If I use the -f option to follow without retries, I get the dreaded "bad file descriptor" issue on STDERR (easy to ignore) and the process terminates immediately. If I use the -F option to include retries it seems to work properly after the bad file descriptor message has come by and it attempts to open the file a second time.

Is there perhaps a more recent win32 build from the coreutils git repository I could try?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不交电费瞎发啥光 2024-12-02 02:11:09

我知道这并不完全是您所要求的,但正如 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);
                  });

I know it is not exatly what you are asking but as James says in the comments, you could do the equivalent functionality directly in c# to save you having to launch another process.

One way you can do it is like this:

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();
    }
}

Then to call for example:

new FollowingTail(new FileInfo(@"C:\test.log"),
                  Encoding.ASCII,
                  s =>
                  {
                      // Do something with the new stuff here, e.g. print it
                      Console.Write(s);
                  });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文