简体   繁体   English

使用.Net Core和System.Diagnostics.Process通过过程管道对Grep和尾巴进行处理

[英]Grep and tail on a huge file with process piping using .Net Core and System.Diagnostics.Process

I want to grep a log file - which can be wery huge, often >= 600mb - and then tail the output to get exactly n lines. 我想grep一个日志文件-可能会很庞大,通常> = 600mb-然后拖尾输出以获得正好n行。 To be more precise I expect to obtain the same output as if I type this command on the Windows/Linux prompt: 更准确地说,我希望获得与在Windows / Linux提示符下键入以下命令相同的输出:

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

I'm a C# and .Net (Core) newbie so be kind with me. 我是C#和.Net(核心)新手,所以请对我好一点。 The following code is my attempt of getting something useful: 以下代码是我尝试获得有用的东西的尝试:

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

I'm pretty sure that the problem could be the fact that using | 我很确定问题可能出在使用| in the raw argument list is't the correct way to handle the pipe between two processes. 原始参数列表中的方法不是处理两个进程之间管道的正确方法。 If I set UseShellExecute to true I get an exception with a message like "UseShellExecute" must always be false . 如果将UseShellExecute设置为true则会出现异常消息,例如"UseShellExecute" must always be false Is there a reasonable and efficient way, even without using external executables, to obtain what I expect? 是否有一种合理而有效的方法,即使不使用外部可执行文件,也可以获得我期望的结果?

If you want to use shell features (metacharacters, pipes, etc), then you should use the shell :) 如果要使用外壳功能(元字符,管道等),则应使用外壳:)

What I mean to say is you should run cmd.exe /c command_line command to achieve what you want. 我的意思是说,您应该运行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();
}

Take special care of quotes and related head-aches 特别注意报价和相关头痛

BTW if the actual need is as simple as the commands you specified, then implementing it directly in C# is a lot easier. 顺便说一句,如果实际需求与您指定的命令一样简单,那么直接在C#中实现它要容易得多。 I provided the solution assuming, you might end up using this with other commands or potentially more command-line arguments or both. 我提供的解决方案是假设,您可能最终将其与其他命令一起使用,或者可能与更多的命令行参数一起使用,或者同时使用两者。

Adding code to do grep and tail in C#. 添加代码以在C#中执行greptail

This is just a simple implementation. 这只是一个简单的实现。 You can benchmark and check if this gets you the desired performance. 您可以进行基准测试,并检查是否可以获得所需的性能。 If not, you can happily stay with the external tools. 如果没有,您可以愉快地使用外部工具。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM