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", "[email protected]")
{
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.OutputString
Out = 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.ErrorString
Error = 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.
or
Pipe` at startup.
Return a Stream
to read the sub-processes' standard error.
Out
must be set to SubProcess.Capture` for this to work.
Return a Stream
to read the sub-processes' standard error.
Error
must 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.