With .NET Core now being a cross-platform framework, it’s very easy to invoke a Bash script from C# codes. It’s not a common practice, but in cases that are lack of a .NET library or REST/rpc API, being able to run a script out-of-process is valuable. So here is a nice extension method that I wrote and found it a joy to call.
public static class ShellHelper | |
{ | |
public static Task<int> Bash(this string cmd, ILogger logger) | |
{ | |
var source = new TaskCompletionSource<int>(); | |
var escapedArgs = cmd.Replace("\"", "\\\""); | |
var process = new Process | |
{ | |
StartInfo = new ProcessStartInfo | |
{ | |
FileName = "bash", | |
Arguments = $"-c \"{escapedArgs}\"", | |
RedirectStandardOutput = true, | |
RedirectStandardError = true, | |
UseShellExecute = false, | |
CreateNoWindow = true | |
}, | |
EnableRaisingEvents = true | |
}; | |
process.Exited += (sender, args) => | |
{ | |
logger.LogWarning(process.StandardError.ReadToEnd()); | |
logger.LogInformation(process.StandardOutput.ReadToEnd()); | |
if (process.ExitCode == 0) | |
{ | |
source.SetResult(0); | |
} | |
else | |
{ | |
source.SetException(new Exception($"Command `{cmd}` failed with exit code `{process.ExitCode}`")); | |
} | |
process.Dispose(); | |
}; | |
try | |
{ | |
process.Start(); | |
} | |
catch (Exception e) | |
{ | |
logger.LogError(e, "Command {} failed", cmd); | |
source.SetException(e); | |
} | |
return source.Task; | |
} | |
} |
To call the method, one can simply do, e.g.:
await $"scripts/00magic.sh --param {arg}".Bash(this.logger); |
nice one. How can I call a .sh file located somewhere local to this service instead of hard coding a command? I want to run multiple commands and one of them takes input so I want to run it in non-interactive mode.