Pipeline Type
An immutable left-to-right chain of commands wired stdout -> stdin, with **no shell** involved: each stage's standard output feeds the next stage's standard input directly. The whole chain runs inside one shared kill-on-dispose group, so cancelling, timing out, or disposing the run reaps every stage together. Build it by piping commands (`a.Pipe(b).Pipe(c)`), then run it to completion with the same run-and-capture verbs a single command exposes (`RunAsync`/`OutputStringAsync`/`OutputBytesAsync`/`ExitCodeAsync`/ `ProbeAsync`/`ParseAsync`/`TryParseAsync`). A pipeline runs as a whole, so the *streaming* verbs (`FirstLineAsync`, `StdoutLinesAsync`) are deliberately not offered — capture the last stage's output instead. The exit status follows shell **pipefail**: the rightmost stage that did not exit with an accepted code (its `Command.OkCodes`, `{0}` by default) determines the result, unless that stage opted out with `Command.UncheckedInPipe`. Per-stage I/O config that applies inside a pipeline: each stage's `OkCodes` (pipefail) and `UncheckedInPipe`, the last stage's `StdoutEncoding`, `StdoutTee`, and `OutputBuffer` **byte** cap (`MaxBytes` + `Overflow`, applied to the captured stdout — its `MaxLines` never applies to a raw byte capture), stage 0's `Stdin` source (feeding the whole chain), and the chain-level `Timeout` / `CancelOn`. Every stage's stderr is likewise drained under its OWN `OutputBuffer`'s **byte** cap (`MaxBytes` + `Overflow`; a stage without `MaxBytes` set keeps its stderr unbounded, as before), so a chatty stage can never exhaust memory regardless of its position in the chain. Per-stage *stdout/ stderr observation* hooks are still **not** applied — intermediate stages' `StdoutTee`, every stage's `StderrTee`, and `OnStdoutLine`/`OnStderrLine` — because the chain wires stdout into the next stage's stdin and captures only the final stage's output. Observe an individual command by running it on its own, not as a pipeline stage. Per-stage config a pipeline cannot honour is **rejected when the stage is piped** (an `ArgumentException` from `Pipe`, naming the field and stage index), rather than silently dropped: a `Stdin` source on any stage *after the first* (its stdin is always rewired to the previous stage's stdout — only stage 0 may set a source), a per-stage `Timeout` on any stage (only the chain-level `Pipeline.Timeout` bounds a pipeline; `Command.Timeout` on a stage never fires), a per-stage `IdleTimeout` on any stage (a pipeline captures only the last stage's output and does not monitor per-stage output activity, so a stage's own idle deadline can never fire), a per-stage `Retry` on any stage (retry is a verb-layer mechanism, and stages spawn directly, bypassing it), and a per-stage `CancelOn` on any stage (a stage's own `Command.CancelOn` token is likewise a verb-layer mechanism the direct stage spawn bypasses; only the chain-level `Pipeline.CancelOn` cancels a pipeline). Set the deadline on the pipeline, cancel the whole chain with `Pipeline.CancelOn`, feed stage 0, or run the command on its own. `MergeStderr` (a shell `2>&1`) is allowed only on the **last** stage — its stdout is the pipeline's captured output, so merging captures the final stage's combined stdout+stderr. On any earlier stage it is rejected (`ArgumentException`) the moment the stage stops being last (another stage is appended after it): a pipeline wires each stage's stdout into the next stage's stdin, so an OS-level merge on an intermediate stage would inject its stderr into the downstream stage's input data. Observability is whole-pipeline, not per-stage: running the chain emits one `Log.spawn`/`Log.exit` pair (plus `Log.timeout` on a timeout) and one `Diag.runStarted`/`runCompleted`/`runEnded` triple, all sharing a single run id — never one set per stage. Stage 0's `Logger` becomes the pipeline's logger (a per-stage `Logger` on any *other* stage has no effect — set it on stage 0, or observe an individual command by running it on its own); the `program` tag/label is a composite of every stage's name, joined `"a | b | c"` (built only from `Command.Program`, never argv/env, so the argv/env-never-logged invariant holds for a multi-stage run too). Per-stage config that is simply **inapplicable** inside a pipeline and has no effect: `StreamBuffer` (a policy for the streaming verbs, which a pipeline does not offer), and `KeepStdinOpen` / `RunningProcess.TakeStdin` (a pipeline exposes no live handle to write stdin into — it wires each stage after the first from the previous stage's stdout itself, and a `KeepStdinOpen` there is an internal wiring detail, not user-reachable).
Instance members
| Instance member |
Description
|
|
Also cancel the whole pipeline when `cancellationToken` fires (in addition to any verb token).
|
Full Usage:
this.ExitCodeAsync
Parameters:
CancellationToken
Returns: Task<Result<int, ProcessError>>
|
The pipefail exit code. A signal kill or timeout errors instead of inventing a sentinel.
|
Full Usage:
this.OutputBytesAsync
Parameters:
CancellationToken
Returns: Task<Result<ProcessResult<byte[]>, ProcessError>>
|
Run the pipeline to completion, capturing the last stage's stdout as raw bytes. A non-zero pipefail exit is data here, not an error.
|
Full Usage:
this.OutputJsonAsync
Parameters:
JsonSerializerOptions
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T |
Require a successful pipefail exit and deserialize the trimmed stdout as JSON into a `'T` via `System.Text.Json` (`options` omitted uses the BCL defaults); invalid JSON becomes `ProcessError.Parse`, just like `ParseAsync`. Give an explicit type argument — there is no parser argument to infer `'T` from. **Trimming / AOT:** deserializes via reflection-based `System.Text.Json` (`JsonSerializer.Deserialize(string, Type, JsonSerializerOptions)`), so it is not trim-/AOT-safe — pass `options` with a source-generated `JsonSerializerContext`/`JsonTypeInfo<'T>` resolver, or avoid this verb, in a trimmed/NativeAOT app.
|
Full Usage:
this.OutputStringAsync
Parameters:
CancellationToken
Returns: Task<Result<ProcessResult<string>, ProcessError>>
|
Run the pipeline to completion, capturing the last stage's stdout as decoded text (using the last stage's stdout encoding). A non-zero pipefail exit is data here, not an error.
|
Full Usage:
this.ParseAsync
Parameters:
Func<string, 'T>
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T |
Require a successful pipefail exit and parse the trimmed stdout into a `'T`; a thrown parser error becomes `ProcessError.Parse`.
|
Append another stage; its stdin is fed from the current last stage's stdout. Rejects (`ArgumentException`) a stage that sets a per-stage `Timeout`/`IdleTimeout`/`Retry`/`CancelOn` or a `Stdin` source — a pipeline cannot honour those (see the type doc); the appended stage is always after the first.
|
|
Full Usage:
this.ProbeAsync
Parameters:
CancellationToken
Returns: Task<Result<bool, ProcessError>>
|
Read the pipefail exit code as a yes/no answer: 0 -> true, 1 -> false, anything else errors.
|
Full Usage:
this.RunAsync
Parameters:
CancellationToken
Returns: Task<Result<string, ProcessError>>
|
Require a successful pipefail exit and return the last stage's stdout, trailing whitespace trimmed. Any checked stage that did not exit 0 fails the pipeline.
|
Full Usage:
this.RunUnitAsync
Parameters:
CancellationToken
Returns: Task<Result<unit, ProcessError>>
|
Like `RunAsync`, but discard the captured output.
|
|
|
Full Usage:
this.TryParseAsync
Parameters:
TryParser<'T>
?cancellationToken : CancellationToken
Returns: Task<Result<'T, ProcessError>>
Type parameters: 'T |
Like `ParseAsync`, but with the standard .NET try-parse shape: pass a BCL parser like `int.TryParse` with an explicit type argument (`TryParseAsync<int>(int.TryParse)` — needed because BCL `TryParse` is overloaded). A `false` return becomes `ProcessError.Parse`. (F# can use the `Result`-returning `Runner.tryParse`.)
|
ProcessKit API Reference