简体   繁体   English

在单个控制台行上处理输出重定向

[英]Process output redirection on a single console line

I am writing a C# application that can launch third party command line executables and redirect their standard output to a RichTextBox. 我正在编写一个C#应用程序,它可以启动第三方命令行可执行文件并将其标准输出重定向到RichTextBox。

I am able to redirect the output alright by using the process.OutputDataReceived event. 我可以通过使用process.OutputDataReceived事件来重定向输出。 I have a queue that I push lines to, and then I use a timer to periodically dump all the queued lines into the RichTextBox. 我有一个将行插入的队列,然后使用计时器将所有排队的行定期转储到RichTextBox中。

This works perfectly for some third party programs. 这非常适合某些第三方程序。

The problem lies with programs that write to a single line of the console multiple times. 问题出在程序多次写入控制台的一行中。 eg: 5% -> 10% -> 25% etc... (constantly rewriting the same spot) This is done, I suppose, by not putting a line feed character, but rather simply returning to the beginning of the line with a carriage return and writing over the previous characters. 例如:5%-> 10%-> 25%等...(不断重写相同的位置)我想这是通过不放置换行符,而是简单地使用a返回到行的开头来完成的。回车并覆盖先前的字符。

What happens is that the OutputDataReceived does not seem to have any way of indicating if a new line should be applied or not so my RichTextBox output simply has multiple updates on new lines. 发生的是,OutputDataReceived似乎没有任何指示是否应应用新行的方法,因此我的RichTextBox输出仅在新行上进行了多次更新。 eg: 5% 10% 25% etc... 例如:5%10%25%等...

Is there any way to tell if the standard output is going on a new line or the same line? 有什么办法可以判断标准输出是在新行还是在同一行?

If I understand correctly, you are currently seeing progress updates reported to your RichTextBox control as individual lines, while you would like to emulate the behavior of the console window. 如果我理解正确,那么您当前会看到进度更新以单独的行报告给RichTextBox控件,而您希望模拟控制台窗口的行为。 Ie to overwrite the previously output line with the new one, rather than keeping the previous line and adding a new one. 即用新的行覆盖先前的输出行,而不是保留前一行并添加新的行。 With that in mind… 考虑到这一点…

The .NET types, including things like TextReader and the Process class's handling of output, consider a newline to be any of \\r , \\n , or \\r\\n . .NET类型(包括TextReaderProcess类的输出处理)将换行符视为\\r\\n\\r\\n So even a process that is simply using a \\r to overwrite the current line would still be interpreted by OutputDataReceived as starting a new line. 因此,即使仅使用\\r覆盖当前行的进程也仍然会被OutputDataReceived解释为开始新行。 Given how consistent this behavior is across .NET, your only real option is to avoid using any built-in line-based I/O mechanisms. 鉴于此行为在.NET中的一致性,您唯一的选择是避免使用任何内置的基于行的I / O机制。

Fortunately, you can get asynchronous reads from the process directly via StandardOutput , instead of having to handle the line-based OutputDataReceived event. 幸运的是,您可以直接通过StandardOutput从流程中异步读取,而不必处理基于行的OutputDataReceived事件。 For example: 例如:

async Task ConsumeOutput(TextReader reader, Action<string> callback)
{
    char[] buffer = new char[256];
    int cch;

    while ((cch = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
    {
        callback(new string(buffer, 0, cch));
    }
}

You might call it like this: 您可以这样称呼它:

// don't "await", but do keep "task" so that at some later time, you can
// check the result, "await" it, whatever, to determine the outcome.
Task task = ConsumeOutput(process.StandardOutput, s =>
    {
        richTextBox1.AppendText(s);
    });

Of course, without any modification the above will just do what you are already getting. 当然,无需进行任何修改,上面的操作就可以完成您已经拥有的功能。 But reading the output this way will not do any interpretation of the line-break characters. 但是以这种方式读取输出不会对换行符产生任何解释。 Instead, they will be present in the string s that is passed to the anonymous method handling each read callback. 相反,它们将出现在字符串s ,该字符串传递给处理每个读取回调的匿名方法。 So you can scan that string yourself, looking for lone carriage returns that indicate that you are starting a new line, but should delete the previous line before adding the new text. 因此,您可以自己扫描该字符串,以查找表明您正在开始新行的单独回车,但是应在添加新文本之前删除前一行。 Be sure to add (or at least skip) all of the text up to the carriage return first, and then delete the last line, before adding the new text. 在添加新文本之前,请确保首先添加(或至少跳过)直到回车的所有文本, 然后删除最后一行。

This code will fix up raw output and make it as it would appear on the terminal. 此代码将修正原始输出,并使它像出现在终端上一样。

 //Deletes all lines that end with only a CR - this emulates the output as it would be seen cmd
public static string DeleteCRLines( this string Str) {
    Str = Str.Replace( "\r\r\n", "\r\n");
    //Splits at the CRLF. We will end up with 'lines' that are full of CR - the last CR-seperated-line is the one we keep
    var AllLines = new List<string>( Str.Split( new[] {"\r\n"}, StringSplitOptions.None));
    for (int i = 0; i < AllLines.Count; i++){
        var CRLines = AllLines[i].Split('\r');
        AllLines[i] = CRLines[ CRLines.Count() -1];
    }
    return String.Join( Environment.NewLine, AllLines.ToArray());
}

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

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