Reading the output

edit

Send the output somewhere

WithOutputStream and WithErrorStream choose where standard output and standard error go while the process runs:

// Stream output line by line
await ProcessWrapper.Create("dotnet")
    .WithArguments("build")
    .WithOutputStream(OutputTarget.ToTextDelegate(line => Console.WriteLine($"[OUT] {line}")))
    .WithErrorStream(OutputTarget.ToTextDelegate(line => Console.Error.WriteLine($"[ERR] {line}")))
    .ExecuteAsync();

// Collect output into a StringBuilder
var sb = new StringBuilder();
await ProcessWrapper.Create("dotnet")
    .WithArguments("build")
    .WithOutputStream(sb)
    .ExecuteAsync();

Console.WriteLine(sb.ToString());

// Stream lines to a TextWriter
using var writer = new StringWriter();
await ProcessWrapper.Create("dotnet")
    .WithArguments("--version")
    .WithOutputStream(OutputTarget.ToTextWriter(writer))
    .ExecuteAsync();

// Collect into a ProcessOutputCollection
var output = new ProcessOutputCollection();
await ProcessWrapper.Create("dotnet")
    .WithArguments("build")
    .WithOutputStream(output)
    .WithErrorStream(output)
    .ExecuteAsync();

foreach (var line in output.StandardError)
{
    Console.Error.WriteLine($"[{line.TimestampUtc:O}] {line.Text}");
}

// Capture raw bytes from stdout/stderr
await using var stdout = File.Create("stdout.bin");
await using var stderr = File.Create("stderr.bin");

await ProcessWrapper.Create("my-command")
    .WithOutputStream(stdout)
    .WithErrorStream(stderr)
    .ExecuteAsync();

// Text and binary handlers can be combined on the same stream
await using var rawOutput = File.Create("raw-output.bin");
await ProcessWrapper.Create("dotnet")
    .WithArguments("--version")
    .WithOutputStream(rawOutput)
    .AddOutputStream(OutputTarget.ToTextDelegate(line => Console.WriteLine($"Version line: {line}")))
    .ExecuteAsync();

// Capture raw bytes using a delegate
await ProcessWrapper.Create("dotnet")
    .WithArguments("--version")
    .WithOutputStream(OutputTarget.ToBytesDelegate(bytes => Console.WriteLine($"Chunk size: {bytes.Length}")))
    .ExecuteAsync();

// Override stdout/stderr decoding when process output is not UTF-8
await ProcessWrapper.Create("my-command")
    .WithOutputEncoding(Encoding.Latin1)
    .WithErrorEncoding(Encoding.Latin1)
    .WithOutputStream(OutputTarget.ToTextDelegate(line => Console.WriteLine(line)))
    .WithErrorStream(OutputTarget.ToTextDelegate(line => Console.Error.WriteLine(line)))
    .ExecuteAsync();

The targets are OutputTarget values. Create them with its static methods, or pass the object directly, which converts implicitly:

TargetCreated withReceives
A callbackOutputTarget.ToTextDelegate, or an Action<string>Each line, without its line break
A TextWriterOutputTarget.ToTextWriter, or the writerEach line, written with WriteLine
A ProcessOutputCollectionOutputTarget.ToProcessOutputCollection, or the collectionEach line, with its stream and the time it was received
A StringBuilderOutputTarget.ToStringBuilder, or the builderThe decoded text, line breaks included
A StreamOutputTarget.ToStream, or the streamThe raw bytes
A byte callbackOutputTarget.ToBytesDelegateEach chunk of raw bytes
A ProcessPipeOutputTarget.ToProcessPipe, or the pipeThe raw bytes, for another process: see Input and pipes
  • With* replaces the targets of the stream, and Add* (AddOutputStream, AddErrorStream) adds targets to the existing ones. Both accept several targets at once.
  • Text and byte targets can be combined on the same stream: each one receives the whole output.
  • A stream that has no target is not redirected: the process writes to the console of the current process.

Several processes can write to the same ProcessOutputCollection, StringBuilder, TextWriter or Stream: the writes are synchronized.

Capture everything

ExecuteBufferedAsync adds a ProcessOutputCollection that receives both streams, and returns it in result.Output (see Getting started). The targets you configured still receive the output.

ProcessOutputCollection is thread-safe. Enumerating it, or its StandardOutput and StandardError properties, enumerates a snapshot. Each ProcessOutput has a Type (StandardOutput or StandardError), a Text and a TimestampUtc, and deconstructs into (type, text) or (type, text, timestampUtc). ToString on the collection returns all the lines, one per line.

Encoding

The output is decoded as UTF-8. Use WithOutputEncoding and WithErrorEncoding for a process that writes another encoding, as in the last example above. The encoding only matters to the text targets: byte targets receive the bytes as the process wrote them.

When a process writes a last line without a line break, the line targets still receive it when the stream closes.