【问题标题】:Execute multiple command lines with output使用输出执行多个命令行
【发布时间】:2015-08-27 14:45:17
【问题描述】:

我想在每次输入后执行一些命令行来显示结果。

    Process p = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;
    p.StartInfo = info;
    p.Start();
    using (StreamWriter sw = p.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
        {
            sw.WriteLine("ftp");
            //output
            sw.WriteLine("open ftp.server.com");
            //output
            sw.WriteLine("username");
            //output
            sw.WriteLine("password");
            //output
        }
    }

帮助我了解如何在每个sw.WriteLine(...)之后制作输出结果?

更新

它不适用于 ftp。为什么?

初始化:

Test test = new Test();
test.start();
Console.ReadKey();

班级Test:

class Test
    {    
        static StringBuilder StdOutput = new StringBuilder();
        Process p = null;
        Queue<string> cmdQueue = new Queue<string>();

        public void start(){

            cmdQueue = new Queue<string>();
            cmdQueue.Enqueue("cd c:\\");
            cmdQueue.Enqueue("dir");

            cmdQueue.Enqueue("ftp");
            cmdQueue.Enqueue("open us1.hostedftp.com");
            cmdQueue.Enqueue("z3r9@ya.ru");
            cmdQueue.Enqueue("123456");
            cmdQueue.Enqueue("dir");
            setupProcess();
            startProcess();    
        }

        private void setupProcess()
        {
            p = new Process();
            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = "cmd";
            info.CreateNoWindow = true;
            info.RedirectStandardOutput = true;
            info.RedirectStandardInput = true;
            info.UseShellExecute = false;

            p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);

            StdOutput = new StringBuilder();

            p.StartInfo = info;
        }

        private async void startProcess()
        {
            p.Start();    
            p.BeginOutputReadLine();                    

            using (StreamWriter sw = p.StandardInput)
            {

                if (sw.BaseStream.CanWrite)
                {
                    while (cmdQueue.Count > 0)
                    { 
                            string cmd = cmdQueue.Dequeue();

                            if (cmd != null & cmd != "")
                            {
                                await sw.WriteLineAsync(cmd);

                                Thread.Sleep(100);

                                //System.Console.WriteLine(StdOutput);
                            }
                            else
                            {
                                break;
                            }  
                    }

                    Console.WriteLine(StdOutput);    
                }                       
                p.WaitForExit();                    
            }
        }

        private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                StdOutput.Append(Environment.NewLine + outLine.Data);    
                //System.Console.WriteLine(Environment.NewLine + outLine.Data);    
            }
        }
    }

【问题讨论】:

    标签: c# cmd output


    【解决方案1】:

    我假设您实际上是在询问如何从 所有 您希望在(一个)进程中执行的命令中捕获 输出

    这是我很久以前提出的解决方案的一个版本,当时我还是这里的菜鸟..

    诀窍是通过侦听来收集输出,当创建输出时Process 将触发的事件:OutputDataReceivedErrorDataReceived。我们需要运行 async 才能使其工作,所以它看起来比通常的示例稍微复杂,通常的示例只有 一个 进程执行 one 命令..:

    首先是几个变量:

        Queue<string> cmdQueue = new Queue<string>();
        static StringBuilder StdOutput = new StringBuilder();
        static StringBuilder ErrOutput = new StringBuilder();
    
        Process p = null;
        Task processTask = null;
        bool processIsRunning = false;
    

    这是一个按钮单击事件,它开始处理来自多行 TextBox 的所有命令。输出在两个StringBuilders 中收集;当队列为空时,我再等一会儿..:

    private void button1_Click(object sender, EventArgs e)
    {
        cmdQueue = new Queue<string>(tb_commands.Lines.ToList());
    
        setupProcess();
        startProcessTask();
    
        while (cmdQueue.Count > 0) Thread.Sleep(100);
        Thread.Sleep(500);
        tb_out.AppendText(StdOutput + "\r\n" + ErrOutput + "\r\n");
    }
    

    这是设置Process 的例程。这里我们注册两个事件,当输出流中有行时会通知我们..:

    private void setupProcess()
    {
        p = new Process();
        ProcessStartInfo info = new ProcessStartInfo();
        info.FileName = "cmd.exe";
        info.CreateNoWindow = true;
        info.RedirectStandardOutput = true;
        info.RedirectStandardError = true;
        info.RedirectStandardInput = true;
        info.UseShellExecute = false;
    
        p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
        p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
        StdOutput = new StringBuilder();
        ErrOutput = new StringBuilder();
        p.StartInfo = info;
    }
    

    设置完成后,我们可以启动一个Task,它将启动我们的Process异步..:

    private void startProcessTask()
    {
        var task = Task.Factory.StartNew(() => startProcess());
        processTask = task;
    }
    

    ..最后是异步方法,它在启动Process 并从重定向流上的异步读取操作开始后,继续从命令队列中提供所有行。

    private async void startProcess()
    {
        try { p.Start(); processIsRunning = true; } catch
        {
            ErrOutput.Append("\r\nError starting cmd process.");
            processIsRunning = false;
        }
    
        p.BeginOutputReadLine();
        p.BeginErrorReadLine();
    
        using (StreamWriter sw = p.StandardInput)
        {
    
            if (sw.BaseStream.CanWrite)
                do
                {
                    try
                    {
                        string cmd = cmdQueue.Dequeue();
                        if (cmd != null & cmd != "") await sw.WriteLineAsync(cmd);
                    } catch { }
                } while (processIsRunning);
            try { p.WaitForExit(); } catch { ErrOutput.Append("WaitForExit Error.\r\n"); }
        }
    }
    

    最后一部分是我们注册的两个事件,用于读取两个流的输出并将它们添加到StringBuilders

    private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {  
            StdOutput.Append(Environment.NewLine + outLine.Data);
        }
    }
    
    private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {  
            ErrOutput.Append(Environment.NewLine + outLine.Data);
        }
    }
    

    请注意,这适用于您可以输入进程的各种命令,包括 FTP。在这里我更改我的代码页,显示我之前的图像,登录到 FTP 服务器,调用帮助页面,cd 和 dir,下载图像,关闭连接并检查我现在拥有的图像..:

    一个警告:我写这篇文章的方式一定有问题,因为 VS 一直在抱怨 System.InvalidOperationException 并且 exe 文件占用了大约 10% 的 CPU。非常感谢帮助我..

    【讨论】:

    • 好吧,你可以看到它正在在这里工作。请注意,只有实际发送到标准输出的输出可以被捕获!
    • 我更新了主帖。我添加了一个示例。ftp 命令不起作用。为什么?请告诉我。
    • 嗯。我对 ftp 没有任何问题。 (我在 win8.1 上并以管理员权限登录))我想您可以手动将相同的命令输入 cmd 窗口?
    • 好。谢谢你。我会试试的。
    • 您好,我现在已经测试了您的版本,至少在 WinForms 程序中我可以正常工作。 only 更改是:使用我的 ftp 测试命令,删除 Console.ReadKey(); 并将 System.Console.WriteLine( Environment.NewLine + outLine.Data); 添加到 OutputDataHandler 以显示输出.. - 我将不得不再次查看这个某个时间,因为您的版本以某种方式没有显示我的问题。但是,您的 ftp 问题可能来自其他地方..
    猜你喜欢
    • 2020-08-04
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 2012-11-24
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多