Tyrrrz / CliWrap

Library for running command-line processes
MIT License
4.32k stars 264 forks source link

ExecuteBufferedAsync - Access a merged Stdout and Sterror #247

Closed MattParkerDev closed 2 months ago

MattParkerDev commented 2 months ago

Details

I'd love to be able to access a merged standard output and standard error on the BufferedCommandResult, as consuming the outputs separately has no way of maintaining ordering of logs. Ideally stdout and sterror are piped to the same pipe?

It could be done with something like

var result = await command.WithStandardErrorPipe(command.StandardOutputPipe).ExecuteBufferedAsync()

however this doesn't work currently

Checklist

Tyrrrz commented 2 months ago

The order cannot be guaranteed in the general case because those are two separate streams. If you run a program that produces and flushes output rapidly to both streams, you will see them come out of order every once in a while.

If you still want to do that, though, I wouldn't recommend merging outputs at the byte or character level, as that can potentially produce mangled results. You can instead initialize a list of strings and append new data only when a complete line has been flushed to any of the streams:

var lines = new List<string>();

await Cli.Wrap(...)
    .WithStandardOutputPipe(PipeTarget.ToDelegate(lines.Add))
    .WithStandardErrorPipe(PipeTarget.ToDelegate(lines.Add))
    .ExecuteAsync();

var mergedOutput = string.Join(Environment.NewLine, lines);

Because any approach to merge output is always going to be a best-effort attempt, and because the specifics of the underlying program and how it's wrapped may affect the desired behavior (for example, if you want real-time processing of merged output, the above solution is suboptimal), I decided to avoid implementing this as a first-party feature.

See also https://github.com/Tyrrrz/CliWrap/issues/202#issuecomment-1546737076