/// <summary> /// exec a file with os shell exec or orbsh shell exec /// </summary> /// <param name="context">command evaluation context</param> /// <param name="comPath">command filePath</param> /// <param name="args">command line arguments string</param> /// <param name="output">shell exec result if any</param> /// <param name="waitForExit">true if wait for exec process exits</param> /// <param name="isStreamsEchoEnabled">if true, exec process output stream is echoized to context out (dump command output)</param> /// <param name="isOutputCaptureEnabled">if true capture the exec process output and give the result in parameter 'output'</param> /// <param name="mergeErrorStreamIntoOutput">if true merge exec process err stream content to the process output content (if process out capture is enabled)</param> /// <returns>exec process return code</returns> public int ShellExec( CommandEvaluationContext context, string comPath, string args, string workingDirectory, out string output, bool waitForExit = true, bool isStreamsEchoEnabled = true, bool isOutputCaptureEnabled = true, bool mergeErrorStreamIntoOutput = true, bool redirectStandardInput = false ) { Thread inputTask = null; TextReader stdin = null; ProcessWrapper pw = null; try { output = null; workingDirectory ??= Environment.CurrentDirectory; var processStartInfo = new ProcessStartInfo() { UseShellExecute = false, //StandardOutputEncoding = Encoding.UTF8, // keep system default //StandardErrorEncoding = Encoding.UTF8, // keep system default RedirectStandardError = true, RedirectStandardInput = redirectStandardInput, // allows access to process StandardInput RedirectStandardOutput = true, CreateNoWindow = true, FileName = comPath, Arguments = args, WindowStyle = ProcessWindowStyle.Normal, WorkingDirectory = workingDirectory }; var sb = new StringBuilder(); // batch shell exec ? if (Path.GetExtension(comPath) == context.ShellEnv.GetValue <string>(ShellEnvironmentVar.settings_clp_shellExecBatchExt)) { var batchMethod = typeof(CommandLineProcessorCommands).GetMethod(nameof(CommandLineProcessorCommands.Batch)); var r = Eval(context, batchMethod, "\"" + FileSystemPath.UnescapePathSeparators(comPath) + " " + args + "\"", 0); output = sb.ToString(); return(r.EvalResultCode); } // process exec pw = ProcessWrapper.ThreadRun( processStartInfo, null, (outStr) => { if (isStreamsEchoEnabled) { context.Out.Echoln(outStr); } if (isOutputCaptureEnabled) { sb.AppendLine(outStr); } }, (errStr) => { if (isStreamsEchoEnabled) { context.Errorln(errStr); } if (isOutputCaptureEnabled && mergeErrorStreamIntoOutput) { sb.AppendLine(errStr); } } ); if (context.ShellEnv.IsOptionSetted(ShellEnvironmentVar.settings_clp_enableShellExecTraceProcessStart)) { context.Out.Echoln($"{context.ShellEnv.Colors.TaskInformation}process '{Path.GetFileName(comPath)}' [{pw.Process.Id}] started(rdc)"); } int retCode = 0; int c = -1; if (waitForExit) { if (redirectStandardInput) { inputTask = new Thread( () => { try { var cp = comPath; stdin = System.Console.In; #if dbg Debug.WriteLine($"input task started [ cp = {cp} ]"); #endif while (!(bool)pw?.Process?.HasExited) { if (System.Console.KeyAvailable) { var key = System.Console.ReadKey(true); c = key.KeyChar; if (c > -1) { context.Out.ConsolePrint("" + (char)c); #if dbg Debug.Write((char)c); #endif pw?.Process.StandardInput.Write((char)c); pw?.Process.StandardInput.Flush(); } } Thread.Sleep(1); } #if dbg Debug.WriteLine("input task exited"); #endif } catch #if dbg (Exception ex) #endif { #if dbg Debug.WriteLine($"input task exited ({ex.Message})"); #endif } }); inputTask.Name = "forward input task"; inputTask.Start(); } var cancellationTask = Task.Run(() => { try { while (!(bool)pw?.Process?.HasExited) { if (IsCancellationRequested) { pw?.Process?.Kill(); } Thread.Sleep(1); } inputTask?.Interrupt(); #if dbg Debug.WriteLine($"cancellation task exited"); #endif } catch #if dbg (Exception ex) #endif { #if dbg Debug.WriteLine($"cancellation task exited ({ex.Message})"); #endif } }); pw.Process.WaitForExit(); retCode = pw.Process.ExitCode; pw.StdOutCallBackThread.Join(); pw.StdErrCallBackThread.Join(); output = sb.ToString(); } if (context.ShellEnv.IsOptionSetted(ShellEnvironmentVar.settings_clp_enableShellExecTraceProcessEnd)) { context.Out.Echoln($"{context.ShellEnv.Colors.TaskInformation}process '{Path.GetFileName(comPath)}' exited with code: {retCode}(rdc)"); } return(retCode); } catch (Exception shellExecException) { inputTask?.Interrupt(); throw new Exception($"ShellExec error: {shellExecException}", shellExecException); } finally { } }