diff --git a/src/Proc/BufferedObservableProcess.cs b/src/Proc/BufferedObservableProcess.cs index 7d839c8..f280a0c 100644 --- a/src/Proc/BufferedObservableProcess.cs +++ b/src/Proc/BufferedObservableProcess.cs @@ -71,17 +71,16 @@ private IDisposable KickOff(IObserver observer) if (!StartProcess(observer)) return Disposable.Empty; Started = true; + _observer = observer; if (Process.HasExited) { - Process.ReadStandardErrBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders()); - Process.ReadStandardOutBlocking(_observer, BufferSize, () => ContinueReadingFromProcessReaders()); + Process.ReadStandardErrBlocking(observer, BufferSize, () => ContinueReadingFromProcessReaders()); + Process.ReadStandardOutBlocking(observer, BufferSize, () => ContinueReadingFromProcessReaders()); OnExit(observer); return Disposable.Empty; } - _observer = observer; - StartAsyncReads(); Process.Exited += (o, s) => diff --git a/src/Proc/Extensions/ArgumentExtensions.cs b/src/Proc/Extensions/ArgumentExtensions.cs index 4cbda3d..aa11417 100644 --- a/src/Proc/Extensions/ArgumentExtensions.cs +++ b/src/Proc/Extensions/ArgumentExtensions.cs @@ -10,10 +10,12 @@ public static string NaivelyQuoteArguments(this IEnumerable arguments) if (arguments == null) return string.Empty; var args = arguments.ToList(); if (args.Count == 0) return string.Empty; + var quotedArgs = args .Select(a => { if (!a.Contains(" ")) return a; + if (a.StartsWith("\"")) return a; return $"\"{a}\""; }) .ToList(); diff --git a/src/Proc/ObservableProcessBase.cs b/src/Proc/ObservableProcessBase.cs index 8a30586..6f1b435 100644 --- a/src/Proc/ObservableProcessBase.cs +++ b/src/Proc/ObservableProcessBase.cs @@ -143,8 +143,8 @@ private Process CreateProcess() var processStartInfo = new ProcessStartInfo { FileName = s.Binary, - #if !NETSTANDARD2_1 && !NET5_0_OR_GREATER - Arguments = args.NaivelyQuoteArguments(), + #if STRING_ARGS + Arguments = args != null ? string.Join(" ", args) : string.Empty, #endif CreateNoWindow = true, UseShellExecute = false, @@ -152,7 +152,7 @@ private Process CreateProcess() RedirectStandardError = true, RedirectStandardInput = true }; - #if NETSTANDARD2_1 || NET5_0_OR_GREATER + #if !STRING_ARGS foreach (var arg in s.Args) processStartInfo.ArgumentList.Add(arg); #endif diff --git a/src/Proc/Proc.Exec.cs b/src/Proc/Proc.Exec.cs index b487fdf..1d1766d 100644 --- a/src/Proc/Proc.Exec.cs +++ b/src/Proc/Proc.Exec.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using ProcNet.Extensions; @@ -26,16 +27,16 @@ public static partial class Proc /// The exit code of the binary being run public static int Exec(ExecArguments arguments) { - var args = arguments.Args.NaivelyQuoteArguments(); + var args = arguments.Args?.ToArray() ?? []; var info = new ProcessStartInfo(arguments.Binary) { UseShellExecute = false }; -#if NETSTANDARD2_1 || NET5_0_OR_GREATER - foreach (var arg in arguments.Args) +#if !STRING_ARGS + foreach (var arg in args) info.ArgumentList.Add(arg); #else - info.Arguments = args; + info.Arguments = args != null ? string.Join(" ", args) : string.Empty; #endif var pwd = arguments.WorkingDirectory; @@ -46,7 +47,7 @@ public static int Exec(ExecArguments arguments) var printBinary = arguments.OnlyPrintBinaryInExceptionMessage ? $"\"{arguments.Binary}\"" - : $"\"{arguments.Binary} {args}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; + : $"\"{arguments.Binary} {args.NaivelyQuoteArguments()}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; using var process = new Process { StartInfo = info }; if (!process.Start()) diff --git a/src/Proc/Proc.ExecAsync.cs b/src/Proc/Proc.ExecAsync.cs index 5a006ad..62545f9 100644 --- a/src/Proc/Proc.ExecAsync.cs +++ b/src/Proc/Proc.ExecAsync.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using ProcNet.Extensions; @@ -18,12 +19,12 @@ public static partial class Proc /// The exit code of the binary being run public static async Task ExecAsync(ExecArguments arguments, CancellationToken ctx = default) { - var args = arguments.Args.NaivelyQuoteArguments(); + var args = arguments.Args?.ToArray() ?? []; var info = new ProcessStartInfo(arguments.Binary) { UseShellExecute = false }; - foreach (var arg in arguments.Args) + foreach (var arg in args) info.ArgumentList.Add(arg); var pwd = arguments.WorkingDirectory; @@ -34,7 +35,7 @@ public static async Task ExecAsync(ExecArguments arguments, CancellationTok var printBinary = arguments.OnlyPrintBinaryInExceptionMessage ? $"\"{arguments.Binary}\"" - : $"\"{arguments.Binary} {args}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; + : $"\"{arguments.Binary} {args.NaivelyQuoteArguments()}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; using var process = new Process { StartInfo = info }; if (!process.Start()) throw new ProcExecException($"Failed to start {printBinary}"); diff --git a/src/Proc/Proc.StartLongRunning.cs b/src/Proc/Proc.StartLongRunning.cs index ada85f8..0021afb 100644 --- a/src/Proc/Proc.StartLongRunning.cs +++ b/src/Proc/Proc.StartLongRunning.cs @@ -104,10 +104,10 @@ public static LongRunningApplicationSubscription StartLongRunning(LongRunningArg var completed = subscription.WaitHandle.WaitOne(waitForStartedConfirmation); if (completed) return subscription; var pwd = arguments.WorkingDirectory; - var args = arguments.Args.NaivelyQuoteArguments(); + var args = arguments.Args; var printBinary = arguments.OnlyPrintBinaryInExceptionMessage ? $"\"{arguments.Binary}\"" - : $"\"{arguments.Binary} {args}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; + : $"\"{arguments.Binary} {args.NaivelyQuoteArguments()}\"{(pwd == null ? string.Empty : $" pwd: {pwd}")}"; throw new ProcExecException($"Could not yield started confirmation after {waitForStartedConfirmation} while running {printBinary}"); } diff --git a/src/Proc/Proc.csproj b/src/Proc/Proc.csproj index 9d729e4..6bd8bde 100644 --- a/src/Proc/Proc.csproj +++ b/src/Proc/Proc.csproj @@ -26,6 +26,7 @@ true Latest README.md + STRING_ARGS diff --git a/tests/Proc.Tests/PrintArgsTests.cs b/tests/Proc.Tests/PrintArgsTests.cs index 3408162..bbf4344 100644 --- a/tests/Proc.Tests/PrintArgsTests.cs +++ b/tests/Proc.Tests/PrintArgsTests.cs @@ -13,6 +13,7 @@ public void ProcSendsAllArguments() AssertOutput(testArgs); } +#if NETSTANDARD2_1 [Fact] public void ArgumentsWithSpaceAreNotSplit() { @@ -33,6 +34,20 @@ public void EscapedQuotes() AssertOutput(testArgs); } +#else + [Fact] + public void EscapedQuotes() + { + string[] testArgs = ["\"this argument has spaces\"", "hello", "world"]; + var args = TestCaseArguments("PrintArgs", testArgs); + var outputWriter = new TestConsoleOutWriter(output); + args.ConsoleOutWriter = outputWriter; + var result = Proc.Start(args); + result.ExitCode.Should().Be(0); + result.ConsoleOut.Should().NotBeEmpty().And.HaveCount(testArgs.Length); + } +#endif + private void AssertOutput(string[] testArgs) { var args = TestCaseArguments("PrintArgs", testArgs);