A System.Diagnostics.Process supercharger: one-liners that run a program, capture or stream its output, and stop it with Ctrl+C before killing it.
var result = Proc.Start("git", "status", "--short"); // streams to the console and captures every linelet sha = shell { return! text $"git rev-parse HEAD" } // each {value} is always one argumentdotnet add package Proc # C#
dotnet add package Proc.Fs # F#: shell { }, exec { } and the Cmd moduleusing ProcNet;
// run a program on the shared console; throws ProcExecException on a non-zero exit code
Proc.Exec("dotnet", "--version");
// capture its output, while still writing it to the console as it arrives
var result = Proc.Start("git", "status", "--short");
foreach (var line in result.ConsoleOut)
Console.WriteLine($"{(line.Error ? "err" : "out")}: {line.Line}");
// start a server, go on once it says it is ready, and stop it with Ctrl+C when disposed
var args = new LongRunningArguments("dotnet", "run", "--project", "api")
{
StartedConfirmationHandler = l => l.Line.Contains("Now listening"),
SendControlCFirst = true
};
using (var api = Proc.StartLongRunning(args, TimeSpan.FromMinutes(1)))
{
Proc.Exec("dotnet", "test", "tests/Api.Tests");
}In F#, a shell { } block runs commands in order and stops at the first one that fails. Commands are interpolated
strings, and each {value} is always one argument, so values never need quoting:
open Proc.Fs
let sha = shell {
sh $"dotnet build"
let! sha = text $"git rev-parse HEAD"
for file in lines $"git ls-files | grep \.fs$" do
sh $"dotnet fantomas {file}"
return sha
}A block can also start a background process and stop it at the end of a scope, run clean-up commands, and run blocks at the same time:
shell {
use! api = background (sh $"dotnet run --project api" |> Cmd.waitFor (fun l -> l.Contains "Now listening"))
use! _ = defer (sh $"docker compose down")
let! _ = concurrently [ steps { sh $"dotnet test tests/A" }; steps { sh $"dotnet test tests/B" } ]
return ()
}Proc.Exec()for the quick one-liners,Proc.ExecAsync()for the same with aCancellationToken.Proc.Start()captures the console output as well as printing it in real time, and lets you script standard input.Proc.StartLongRunning()starts a server or database and returns once it prints that it is ready.ObservableProcessandEventBasedObservableProcesswrapSystem.Diagnostics.Processas anIObservable.ObservableProcesslistens on the output buffers directly, so it sees prompts that never end in a newline.SendControlCFirst = truesendsSIGINTbefore falling back to a hardSIGKILL(Process.Kill()), so a process can shut down cleanly.- On .NET 11 it uses the new Process APIs: in-process
SIGINTon Linux and macOS,KillOnParentExitandInheritedHandles.
| Package | What it is | |
|---|---|---|
Proc |
the C# API: Proc.Exec, Proc.Start, Proc.StartLongRunning and the observable processes |
|
Proc.Fs |
F# bindings: shell { }, exec { }, the Cmd module and pipes |
- Proc reference: every call and option,
SIGINThandling, line boundaries and .NET 11 - Proc.Fs reference:
shell { }, background processes,shquoting rules,Cmd, pipes andexec { } - Releases
- Contributing
MIT licensed. Copyright Nullean and contributors.