Class For Executing Ubuntu Commands From C# Code
Wed Jan 11 2023 09:45:11 GMT+0000 (Coordinated Universal Time)
Saved by @HristoT
var bash = new BashShell();
var result = await bash.RunOrThrow($"<command here>");
===================================================================================================
public class BashShell
{
private readonly string workingDirectory;
public event Action<string> OnStdOut;
public event Action<string> OnStdErr;
public BashShell(string workingDirectory)
{
if (string.IsNullOrWhiteSpace(workingDirectory))
{
this.workingDirectory = Environment.CurrentDirectory;
}
else
{
this.workingDirectory = workingDirectory;
}
}
public BashShell() : this(null)
{
}
public async Task<ShellResult> Run(string command)
{
var process = new Process
{
StartInfo = new ProcessStartInfo("/usr/bin/env")
{
Arguments = "bash",
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = this.workingDirectory
},
};
var asyncLock = new AsyncLock();
var stdOut = new StringBuilder();
var stdErr = new StringBuilder();
int exitCode;
using (process)
{
process.Start();
var outTask = Task.Run(async () =>
{
using var reader = process.StandardOutput;
while (!reader.EndOfStream || !process.HasExited)
{
string line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line))
{
continue;
}
using (await asyncLock.Lock())
{
this.OnStdOut?.Invoke(line);
stdOut.AppendLine(line);
}
}
});
var errTask = Task.Run(async () =>
{
using var reader = process.StandardError;
while (!reader.EndOfStream || !process.HasExited)
{
string line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line))
{
continue;
}
using (await asyncLock.Lock())
{
this.OnStdErr?.Invoke(line);
stdErr.AppendLine(line);
}
}
});
await process.StandardInput.WriteLineAsync("/usr/bin/env bash");
await process.StandardInput.WriteLineAsync("set -ex");
await process.StandardInput.WriteLineAsync(command);
await process.StandardInput.WriteLineAsync("exit \"$?\"");
await process.StandardInput.WriteLineAsync("exit \"$?\"");
await Task.WhenAll(outTask, errTask);
exitCode = process.ExitCode;
}
return new ShellResult
{
Command = command,
ExitCode = exitCode,
StdErr = stdErr.ToString(),
StdOut = stdOut.ToString()
};
}
public async Task<ShellResult> RunOrThrow(string command)
{
var result = await this.Run(command);
result.ThrowIfFailed();
return result;
}
}
public class ShellResult
{
public int ExitCode { get; set; }
public string StdErr { get; set; }
public string StdOut { get; set; }
public string Command { get; set; }
public void ThrowIfFailed()
{
if (this.ExitCode != 0)
{
throw new ApplicationException("A bash command exited with non zero code.");
}
}
}
public class AsyncLock
{
private readonly SemaphoreSlim semaphore;
public AsyncLock()
{
this.semaphore = new SemaphoreSlim(1, 1);
}
public async Task<AsyncLockInstance> Lock()
{
await this.semaphore.WaitAsync();
return new AsyncLockInstance(this.semaphore);
}
}



Comments