A library to run sub-processes on .net
By Gene Thomas
Gt.SubProcess is on nuget - Install-Package Gt.SubProcess.
The Process class has a unusable string StartInfo.Arguments,
the rules around quoting "s are complex so one can not just concatenate arguments to be passed in.
SubProcess takes a list of strings and handles the quoting internally.
By default the standard output and standard error are captured into strings, accessible as
as .OutputString and ErrorString after the sub-process has started.
using System;
using System.IO;
using Gt.SubProcess;
namespace ReadmeExample
{
class Program
{
static int Main(string[] args) {
try {
SubProcess.Call("ls", "-l");
SubProcess.CheckCall("psql", "my-database", "fred");
// throws if exits with non 0 exit code
SubProcess p = new SubProcess("ssh", "me@mysite.com")
{
Out = new FileStream("ssh-output.txt", FileMode.OpenOrCreate),
Error = SubProcess.Capture,
In = SubProcess.Pipe
};
p.Wait();
Console.WriteLine(p.ErrorString);
return 0;
} catch (Exception e) {
Console.Error.WriteLine("Fatal Error: " + e.Message);
return 1;
}
}
}
}SubProcess has a number of static methods which do not require one to create a SubProcess object.
SubProcess.Call("ls", "-l", "-r", "-t") and SubProcess.Call(IEnumerable<string>) run the program and return it's int return code.
Output is not captured.
SubProcess.CheckCall("ls", "-l", "-r", "-t") and SubProcess.CheckCall(IEnumerable<string>) run the program and throw a Failed exception if the program exits with a non zero exit code.
SubProcess.CheckOutput("ls", "-l", "-r", "-t").With(expectedString) and CheckOutput(IEnumerable<string>).With(expectedString) run the program, throw
Failed exception if it exits with a non-zero exide code and throw an UnexpectedOutput exception if the program's standed output is not as expected,
as specified in the .With(string) part.
If you need more control over the sub-process create and intance of SubProcess and initialise as required.
SubProcess objects are created with a list of strings inline, e.g. var p = new SubProcess("ls", "-l") or with an IEnumerable<string>, e.g. a List<string>.
There a number of methods and properties that can be used to launch and run the program as required.
Set how the subprocess's input is handled:
In = new FileStream("input.txt", FileMode.Open);
a streamIn = SubProcess.Through
Read from the terminalIn = SubProcess.Swallow
no inputIn = SubProcess.Pipe
the default
allowsWrite()/WriteLine()of string or byte[]s
Set how the subprocess's output is handled:
Out = new FileStream("output.txt", FileMode.OpenOrCreate);
a streamOut = SubProcess.Through
let the output be printed to the terminalOut = SubProcess.Capture
the default
to string
string output = subprocess.OutputStringOut = SubProcess.Swallow
throw awayOut = SubProcess.Pipe
read incremenally
Stream stream = subprocess.OutputStream
TextReader reader = subprocess.OutputReader
Set how the subprocess's error output is handled:
Error = new FileStream("error.txt", FileMode.OpenOrCreate);
a streamError = SubProcess.Through
let the error output be printed to the terminalError = SubProcess.Capture
the default
to string
string errorMessage = subprocess.ErrorStringError = SubProcess.Swallow
throw awayError = SubProcess.ToOut
redirect Error to OutError = SubProcess.Pipe
read incremenally
Stream stream = subprocess.ErrorStream
TextReader reader = subprocess.ErrorReader
Set the directory to start the process in.
Set environment variabled for the process. e.g.
new SubProcess("my-app") {
Environment = {
["NAME"] = "VALUE"
}
};Timeout after given number of seconds.
Throw TimeoutException if the process does not exit in the given time.
Add an item to the process arguments list.
e.g. p.Add("--verbose");
Add an item to the process arguments list.
e.g. p.Add("--name", "fred");
Returns true if the sub-process has started.
Returns the argument list formatted using quotes as required.
Set the sub-process running.
Wait for the process to exit, Start()ing if required. Returns the processes' exit code.
Wait for the process to exit, Start()ing if required. Throw a Failed if the sub-process exits with a non-zero exit code.
Terminate the sub-process. This happens asynchronously, we do not wait for the sub-process to exit.
The number that the sub-process exited with. Traditionally 0 means a good exit, and non-zero means a problem occured.
Returns true of the sub-process has run and exited.
Returns true of the sub-process is still runnning.
Returns the name of the sub-process.
Write the given string to the sub-processes' standard input. The SubProcess must have been started with In = SubProcess.Pipe,
the default.
Write the given bytes, from offset and of length count to the processes' standard input.
The SubProcess must have been started with In = SubProcess.Pipe,
Write the given string to the sub-processes' standard input and append a newline.
The SubProcess must have been started with In = SubProcess.Pipe, the default.
Return a string containing the sub-processes' captured standard output.
The encoding defaults to UTF-8 but can be set using the startup OutputEncoding propery.
The Out property must be set to SubProcess.Capture or SubProcess.Pipe at startup.
Return a string containing the sub-processes' captured standard error.
The encoding defaults to UTF-8 but can be set using the startup OutputEncoding propery.
The Error property must be set to SubProcess.Capture or SubProcess.Pipe at startup.
Return a TextReader to read the sub-processes' standard output.
The encoding defaults to UTF-8 but can be set using the startup OutputEncoding propery.
The Out property must be set to Pipe for this to wok.
Return a TextReader to read the sub-processes' standard error.
The encoding defaults to UTF-8 but can be set using the startup ErrorEncoding propery.
The Error property must be set to Capture or Pipe at startup.
orPipe` at startup.
Return a Stream to read the sub-processes' standard error.
Outmust be set to SubProcess.Capture` for this to work.
Return a Stream to read the sub-processes' standard error.
Errormust be set to SubProcess.Pipe` for this to work.
Return true if the sub-process took too long.
Asynchronous version of Check(). Wait for the process to exit, Start()ing if required.
Throw a Failed if the sub-process exits with a non-zero exit code.
SubProcess inherits from IDisposable so can be used with a using block to ensure that the sub-process is terminated.
e.g.
using (SubProcess p = new SubProcess("find", ".", "-name", "*.exe"))
{
// do something
}Kill() is used if we leave the using block without p exiting.