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