[API Proposal]: Process.StartRedirected
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Background and Motivation
This proposal provides a one-liner for the common pattern of starting a process with redirected streams to consume them in the app. Today this looks like:
```csharp
var psi = new ProcessStartInfo("dotnet", ["build"])
{
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
};
var process = Process.Start(psi)!;
process.StandardInput.Close(); // prevent child from blocking on stdin
```
This pattern has:
1. **Boilerplate**: Three boolean properties must be set on `ProcessStartInfo` just to opt into redirection.
2. **Forgetting to close stdin**: If the child process reads from stdin and the parent never writes, the child blocks forever. Closing stdin immediately after start is a widespread pattern but easy to forget.
3. **Null-return**: `Process.Start(ProcessStartInfo)` returns `Process?` — the nullable return is a holdover from `UseShellExecute` scenarios and forces a `!` or null-check that is never meaningful when redirecting.
## API Proposal
```csharp
namespace System.Diagnostics
{
public class Process : System.ComponentModel.Component, System.IDisposable
{
public static Process StartRedirected(
ProcessStartInfo startInfo,
bool closeInput = true);
public static Process StartRedirected(
string fileName,
IList? arguments = null,
bool closeInput = true);
}
}
```
**Parameters:**
| Parameter | Description |
|-----------|-------------|
| `startInfo` | A `ProcessStartInfo` describing the process to start. The `Redirect*` properties are ignored (redirection is always enabled). `UseShellExecute` must be `false` (the default) and `StandardInputHandle`/`StandardOutputHandle`/`StandardErrorHandle` must be `null` (the default). |
| `closeInput` | When `true` (default), standard input is redirected and immediately closed, preventing the child from blocking on stdin. When `false`, standard input is redirected and left open — the caller can write to `process.StandardInput`. |
**Return value:** A non-null `Process` instance with `StandardOutput` and `StandardError` ready for reading (and `StandardInput` available for writing when `closeInput` is `false`).
## API Usage
**Stream output line-by-line (most common case):**
```csharp
using var process = Process.StartRedirected("dotnet", ["build"]);
try
{
await foreach (var line in process.ReadAllLinesAsync())
{
Console.WriteLine($"[build] {line}");
}
process.WaitForExit();
}
catch
{
process.Kill();
throw;
}
```
**Write input to the child process:**
```csharp
using var process = Process.StartRedirected("sort", closeInput: false);
process.StandardInput.WriteLine("banana");
process.StandardInput.WriteLine("apple");
process.StandardInput.Close(); // signal EOF to child
```
**Use `ProcessStartInfo` for additional configuration:**
```csharp
var psi = new ProcessStartInfo("dotnet", "test")
{
WorkingDirectory = "/src/myproject",
KillOnParentExit = true,
};
psi.Environment["DOTNET_CLI_TELEMETRY_OPTOUT"] = "1";
using var process = Process.StartRedirected(psi);
```
Contributor guide
Assessment
This issue has not been assessed yet.