예제 #1
0
        /// <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
            {
            }
        }