使用.Net Core和System.Diagnostics.Process在带有过程管道的巨大文件上进行grep和tail

时间:2017-04-03 16:16:28

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

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

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

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

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之类的消息的异常。 有没有一种合理有效的方法,即使不使用外部可执行文件,也能获得我的期望?

1 个答案:

答案 0 :(得分:2)

如果你想使用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();
}

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

BTW如果实际需要与您指定的命令一样简单,那么直接在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();
}