90 lines
No EOL
2.2 KiB
C#
90 lines
No EOL
2.2 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
internal static class Shellify
|
|
{
|
|
[Flags]
|
|
private enum ShellSplitState
|
|
{
|
|
None = 0,
|
|
InSingleQuote = 1 << 0,
|
|
InDoubleQuote = 1 << 1,
|
|
Escape = 1 << 2
|
|
}
|
|
|
|
/// <summary>
|
|
/// Splits a shell-escaped string into arguments, handling quotes and escapes.
|
|
/// </summary>
|
|
public static List<string> ShellSplit(string input)
|
|
{
|
|
var args = new List<string>();
|
|
if (string.IsNullOrEmpty(input)) return args;
|
|
var current = new System.Text.StringBuilder();
|
|
var state = ShellSplitState.None;
|
|
foreach (char c in input)
|
|
{
|
|
if (state.HasFlag(ShellSplitState.Escape))
|
|
{
|
|
current.Append(c);
|
|
state &= ~ShellSplitState.Escape;
|
|
}
|
|
else if (c == '\\')
|
|
{
|
|
state |= ShellSplitState.Escape;
|
|
}
|
|
else if (c == '\'' && !state.HasFlag(ShellSplitState.InDoubleQuote))
|
|
{
|
|
state ^= ShellSplitState.InSingleQuote;
|
|
}
|
|
else if (c == '"' && !state.HasFlag(ShellSplitState.InSingleQuote))
|
|
{
|
|
state ^= ShellSplitState.InDoubleQuote;
|
|
}
|
|
else if (char.IsWhiteSpace(c) && !state.HasFlag(ShellSplitState.InSingleQuote) && !state.HasFlag(ShellSplitState.InDoubleQuote))
|
|
{
|
|
if (current.Length > 0)
|
|
{
|
|
args.Add(current.ToString());
|
|
current.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
current.Append(c);
|
|
}
|
|
}
|
|
|
|
if (current.Length > 0)
|
|
args.Add(current.ToString());
|
|
return args;
|
|
}
|
|
|
|
public static TaskAwaiter<string> GetAwaiter(this string command)
|
|
{
|
|
return RunProcessAsync(command).GetAwaiter();
|
|
}
|
|
|
|
private static async Task<string> RunProcessAsync(string command)
|
|
{
|
|
var strings = ShellSplit(command);
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = strings.First(),
|
|
ArgumentList =
|
|
{
|
|
strings.Skip(1)
|
|
},
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true,
|
|
};
|
|
using var process = Process.Start(psi);
|
|
string output = await process.StandardOutput.ReadToEndAsync();
|
|
string error = await process.StandardError.ReadToEndAsync();
|
|
await process.WaitForExitAsync();
|
|
if (process.ExitCode != 0)
|
|
throw new System.Exception($"Process exited with code {process.ExitCode}: {error}");
|
|
return output;
|
|
}
|
|
} |