【问题标题】:Grep and tail on a huge file with process piping using .Net Core and System.Diagnostics.Process使用 .Net Core 和 System.Diagnostics.Process 对带有流程管道的巨大文件进行 grep 和 tail
【发布时间】:2017-04-03 16:16:28
【问题描述】:

我想 grep 一个日志文件 - 它可能非常大,通常 >= 600mb - 然后尾随输出以获得正好 n 行。 更准确地说,我希望获得与在 Windows/Linux 提示符下键入以下命令相同的输出:

grep.exe -i "STRING" "PATH_TO_FILE" | tail.exe -n LINES_TO_TAIL

我是 C# 和 .Net (Core) 新手,所以请善待我。以下代码是我尝试获得有用的东西:

executableName = @"PATH_TO\grep.exe";
executableArgs = String.Format(@"-i {0} {1} | PATH_TO\\tail.exe -n {2}", paramList["Text"], moduleInfo.GetValue("LogPath"), paramList["MaxLines"]);

var processStartInfo = new ProcessStartInfo
{
    FileName = executableName,
    Arguments = executableArgs,
    RedirectStandardOutput = true
};

Process process = Process.Start(processStartInfo);
string output = process.StandardOutput.ReadToEnd();

process.WaitForExit();

我很确定问题可能在于在原始参数列表中使用| 不是处理两个进程之间的管道的正确方法。 如果我将UseShellExecute 设置为true,我会收到一条异常消息,如"UseShellExecute" must always be false。 即使不使用外部可执行文件,是否有合理有效的方法来获得我的期望?

【问题讨论】:

  • 问题是 executableArgs 将作为参数传递,这不像在控制台中编写。您需要为tail 创建另一个进程,并将输入作为grep 进程的输出提供给它。
  • 我找到了this。我认为这正是您想要的。

标签: c# asp.net-core .net-core


【解决方案1】:

如果你想使用 shell 特性(元字符、管道等),那么你应该使用 shell :)

我的意思是你应该运行cmd.exe /c command_line 命令来实现你想要的。

var pathToGrep = '"' + @"PATH_TO_GREP\grep.exe" + '"';
var pathToTail = '"' + @"PATH_TO_TAIL\tail.exe" + '"';
var pathToLogFile = '"' + @"PATH_TO_LOG\ganttproject.log" + '"';
var cmd = '"' + $@"{pathToGrep} -i SEARCH_TEXT {pathToLogFile} | {pathToTail} -n 10" + '"';
var processStartInfo = new ProcessStartInfo {
    FileName = @"C:\Windows\System32\cmd.exe",
    Arguments = $@"/c {cmd}",
    CreateNoWindow = true,
    RedirectStandardOutput = true,
    UseShellExecute = false
};

using (var process = Process.Start(processStartInfo)) {
    process.WaitForExit();
    var output = process.StandardOutput.ReadToEnd();
}

特别注意引号和相关的头痛

顺便说一句,如果实际需要与您指定的命令一样简单,那么直接在 C# 中实现它会容易得多。我提供的解决方案假设您最终可能会将此与其他命令或可能更多的命令行参数或两者一起使用。

在 C# 中添加代码以执行 greptail

这只是一个简单的实现。您可以进行基准测试并检查这是否可以获得所需的性能。如果没有,您可以愉快地使用外部工具。

class TailQueue<T> {
    readonly T[] Buffer;
    bool Full = false;
    int Head = -1;

    public TailQueue(int quesize) {
        Buffer = new T[quesize];
    }

    public void Enqueue(T elem) {
        Head = Head == Buffer.Length - 1 ? 0 : Head + 1;
        Buffer[Head] = elem;
        if (Head == Buffer.Length - 1)
            Full = true;
    }

    public IEnumerable<T> GetAll() {
        if (Head == -1)
            yield break;

        var startIndex = 0;
        if (Full && Head != Buffer.Length - 1)
            startIndex = Head + 1;

        for (int i = startIndex; i <= Head; i = (i + 1) % Buffer.Length) {
            yield return Buffer[i];
        }
    }
}

static IEnumerable<string> GrepTail(string filePath, string expression, int lineCount) {
    var lineQ = new TailQueue<string>(lineCount);

    foreach (var line in File.ReadLines(filePath)) {
        if (line.IndexOf(expression, StringComparison.OrdinalIgnoreCase) != -1)
            lineQ.Enqueue(line);
    }

    return lineQ.GetAll();
}

【讨论】:

  • "那么直接在 C# 中实现就容易多了" 那么性能呢? “本机”C# 实现至少会比使用原始 grep 快吗?
  • 我还没有进行基准测试,但如果您愿意,我可以编写一些代码供您尝试
猜你喜欢
  • 2011-01-22
  • 2022-01-27
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 2013-03-28
  • 2015-12-14
  • 2011-06-07
  • 1970-01-01
相关资源
最近更新 更多