Как захватить вывод команды Shell в C #? - PullRequest
16 голосов
/ 03 января 2011

Сводка:

  • реестр запросов на удаленной машине
  • захватывать вывод для использования в приложении
  • должен быть в csharp
  • до сих порвсе используемые методы могут запрашивать только на локальном компьютере
  • любая надежда приветствуется

Полная версия:

Мне нужно найти способ выполнить команду командной строкив csharp и захватить его вывод.Я знаю, как сделать это в Perl, ниже приведен код, который я использовал бы в Perl.

#machine to check
my $pc = $_[0];
#create location of registry query
my $machine = "\\\\".$pc."\\HKEY_USERS";
#run registry query
my @regQuery= `REG QUERY $machine`;

Любые предложения о том, как сделать это в csharp, будут приветствоваться.До сих пор я пытался использовать метод RegistryKey OurKey = Registry.Users, и он прекрасно работает, но я не могу запросить реестр на удаленной машине.

Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

РЕШЕНИЕ: (Спасибо @Robaticus)

private void reg(string host)
        {

            string build = "QUERY \\\\" + host + "\\HKEY_USERS";
            string parms = @build;
            string output = "";
            string error = string.Empty;

            ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);

            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
            psi.UseShellExecute = false;
            System.Diagnostics.Process reg;
            reg = System.Diagnostics.Process.Start(psi);
            using (System.IO.StreamReader myOutput = reg.StandardOutput)
            {
                output = myOutput.ReadToEnd();
            }
            using (System.IO.StreamReader myError = reg.StandardError)
            {
                error = myError.ReadToEnd();

            }
            Output.AppendText(output + "\n");


        }  

Ответы [ 5 ]

27 голосов
/ 03 января 2011

Возможно, вам придется немного изменить это, но вот некоторый (слегка измененный по сравнению с оригинальным) код, который перенаправляет stdout и stderr для процесса:

        string parms = @"QUERY \\machine\HKEY_USERS";
        string output = "";
        string error = string.Empty;

        ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);

        psi.RedirectStandardOutput = true;
        psi.RedirectStandardError = true;
        psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
        psi.UseShellExecute = false;
        System.Diagnostics.Process reg;
        reg = System.Diagnostics.Process.Start(psi);
        using (System.IO.StreamReader myOutput = reg.StandardOutput)
        {
            output = myOutput.ReadToEnd();
        }
        using(System.IO.StreamReader myError = reg.StandardError)
        {
            error = myError.ReadToEnd();

        }
8 голосов
/ 03 января 2011

Практически все, что вы можете запустить в командной строке, вы можете запустить в программе на C # с аналогичными ограничениями. Есть несколько способов сделать это, один из них с помощью команд асинхронного процесса, как я показываю в моем блоге . Вы просто пишете и читаете в командной строке в активном режиме. Отсюда просто выясните, чего вы хотите достичь и как это сделать с помощью командной строки. Затем подключите его к программе

class Program
{
static void Main(string[] args)
{
LaunchCommandAsProcess cmd = new LaunchCommandAsProcess();
cmd.OutputReceived += new LaunchCommandAsProcess.OutputEventHandler(launch_OutputReceived);
cmd.SendCommand("help");
cmd.SendCommand("ipconfig");
cmd.SyncClose();
}
/// Outputs normal and error output from the command prompt.
static void launch_OutputReceived(object sendingProcess, EventArgsForCommand e)
{
Console.WriteLine(e.OutputData);
}
}

Как видите, вы просто создаете экземпляр класса, обрабатываете выходное событие и начинаете писать команды так же, как вы печатали в командной строке.

Вот как это работает:

public class LaunchCommandAsProcess
{
public delegate void OutputEventHandler(object sendingProcess, EventArgsForCommand e);
public event OutputEventHandler OutputReceived;
private StreamWriter stdIn;
private Process p;
public void SendCommand(string command)
{
stdIn.WriteLine(command);
}
public LaunchCommandAsProcess()
{
p = new Process();
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();

stdIn = p.StandardInput;
p.OutputDataReceived += Process_OutputDataReceived;
p.ErrorDataReceived += Process_OutputDataReceived;
p.BeginOutputReadLine();
p.BeginErrorReadLine();

}
///
/// Raises events when output data has been received. Includes normal and error output.
/// 

/// /// private void Process_OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine.Data == null)
return;
else
{
if (OutputReceived != null)
{
EventArgsForCommand e = new EventArgsForCommand();
e.OutputData = outLine.Data;
OutputReceived(this, e);
}
}
}
///
/// Synchronously closes the command promp.
/// 

public void SyncClose()
{
stdIn.WriteLine("exit");
p.WaitForExit();
p.Close();
}
///
/// Asynchronously closees the command prompt.
/// 

public void AsyncClose()
{
stdIn.WriteLine("exit");
p.Close();
}
}
public class EventArgsForCommand : EventArgs
{
public string OutputData { get; internal set; }
}
4 голосов
/ 03 января 2011

Вот класс, который я использую. Он адаптирован из кода, который я нашел в блоге некоторое время назад, но с различными другими модификациями.

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace SonomaTechnologyInc {
    /// <summary>
    /// Utility class for working with command-line programs.
    /// </summary>
    public class Subprocess {  
        private Subprocess() { }

        /// <summary>
        /// Executes a command-line program, specifying a maximum time to wait
        /// for it to complete.
        /// </summary>
        /// <param name="command">
        /// The path to the program executable.
        /// </param>
        /// <param name="args">
        /// The command-line arguments for the program.
        /// </param>
        /// <param name="timeout">
        /// The maximum time to wait for the subprocess to complete, in milliseconds.
        /// </param>
        /// <returns>
        /// A <see cref="SubprocessResult"/> containing the results of
        /// running the program.
        /// </returns>
        public static SubprocessResult RunProgram(string command, string args, int timeout) {
            bool timedOut = false;
            ProcessStartInfo pinfo = new ProcessStartInfo(command);
            pinfo.Arguments = args;
            pinfo.UseShellExecute = false;
            pinfo.CreateNoWindow = true;
            //pinfo.WorkingDirectory = ?
            pinfo.RedirectStandardOutput = true;
            pinfo.RedirectStandardError = true;
            Process subprocess = Process.Start(pinfo);

            ProcessStream processStream = new ProcessStream();
            try {
                processStream.Read(subprocess);

                subprocess.WaitForExit(timeout);
                processStream.Stop();
                if(!subprocess.HasExited) {
                    // OK, we waited until the timeout but it still didn't exit; just kill the process now
                    timedOut = true;
                    try {
                        subprocess.Kill();
                        processStream.Stop();
                    } catch { }
                    subprocess.WaitForExit();
                }
            } catch(Exception ex) {
                subprocess.Kill();
                processStream.Stop();
                throw ex;
            } finally {
                processStream.Stop();
            }

            TimeSpan duration = subprocess.ExitTime - subprocess.StartTime;
            float executionTime = (float) duration.TotalSeconds;
            SubprocessResult result = new SubprocessResult(
                executionTime, 
                processStream.StandardOutput.Trim(), 
                processStream.StandardError.Trim(), 
                subprocess.ExitCode, 
                timedOut);
            return result;
        }
    }

    /// <summary>
    /// Represents the result of executing a command-line program.
    /// </summary>
    public class SubprocessResult {
        readonly float executionTime;
        readonly string stdout;
        readonly string stderr;
        readonly int exitCode;
        readonly bool timedOut;

        internal SubprocessResult(float executionTime, string stdout, string stderr, int exitCode, bool timedOut) {
            this.executionTime = executionTime;
            this.stdout = stdout;
            this.stderr = stderr;
            this.exitCode = exitCode;
            this.timedOut = timedOut;
        }

        /// <summary>
        /// Gets the total wall time that the subprocess took, in seconds.
        /// </summary>
        public float ExecutionTime {
            get { return executionTime; }
        }

        /// <summary>
        /// Gets the output that the subprocess wrote to its standard output stream.
        /// </summary>
        public string Stdout {
            get { return stdout; }
        }

        /// <summary>
        /// Gets the output that the subprocess wrote to its standard error stream.
        /// </summary>
        public string Stderr {
            get { return stderr; }
        }

        /// <summary>
        /// Gets the subprocess's exit code.
        /// </summary>
        public int ExitCode {
            get { return exitCode; }
        }

        /// <summary>
        /// Gets a flag indicating whether the subprocess was aborted because it
        /// timed out.
        /// </summary>
        public bool TimedOut {
            get { return timedOut; }
        }
    }

    internal class ProcessStream {
        /*
         * Class to get process stdout/stderr streams
         * Author: SeemabK (seemabk@yahoo.com)
         * Usage:
            //create ProcessStream
            ProcessStream myProcessStream = new ProcessStream();
            //create and populate Process as needed
            Process myProcess = new Process();
            myProcess.StartInfo.FileName = "myexec.exe";
            myProcess.StartInfo.Arguments = "-myargs";

            //redirect stdout and/or stderr
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo.RedirectStandardError = true;

            //start Process
            myProcess.Start();
            //connect to ProcessStream
            myProcessStream.Read(ref myProcess);
            //wait for Process to end
            myProcess.WaitForExit();

            //get the captured output :)
            string output = myProcessStream.StandardOutput;
            string error = myProcessStream.StandardError;
         */
        private Thread StandardOutputReader;
        private Thread StandardErrorReader;
        private Process RunProcess;
        private string _StandardOutput = "";
        private string _StandardError = "";

        public string StandardOutput {
            get { return _StandardOutput; }
        }
        public string StandardError {
            get { return _StandardError; }
        }

        public ProcessStream() {
            Init();
        }

        public void Read(Process process) {
            try {
                Init();
                RunProcess = process;

                if(RunProcess.StartInfo.RedirectStandardOutput) {
                    StandardOutputReader = new Thread(new ThreadStart(ReadStandardOutput));
                    StandardOutputReader.Start();
                }
                if(RunProcess.StartInfo.RedirectStandardError) {
                    StandardErrorReader = new Thread(new ThreadStart(ReadStandardError));
                    StandardErrorReader.Start();
                }

                int TIMEOUT = 1 * 60 * 1000; // one minute
                if(StandardOutputReader != null)
                    StandardOutputReader.Join(TIMEOUT);
                if(StandardErrorReader != null)
                    StandardErrorReader.Join(TIMEOUT);

            } catch { }
        }

        private void ReadStandardOutput() {
            if(RunProcess == null) return;
            try {
                StringBuilder sb = new StringBuilder();
                string line = null;
                while((line = RunProcess.StandardOutput.ReadLine()) != null) {
                    sb.Append(line);
                    sb.Append(Environment.NewLine);
                }
                _StandardOutput = sb.ToString();
            } catch { }
        }

        private void ReadStandardError() {
            if(RunProcess == null) return;
            try {
                StringBuilder sb = new StringBuilder();
                string line = null;
                while((line = RunProcess.StandardError.ReadLine()) != null) {
                    sb.Append(line);
                    sb.Append(Environment.NewLine);
                }
                _StandardError = sb.ToString();
            } catch { }
        }

        private void Init() {
            _StandardError = "";
            _StandardOutput = "";
            RunProcess = null;
            Stop();
        }

        public void Stop() {
            try { if(StandardOutputReader != null) StandardOutputReader.Abort(); } catch { }
            try { if(StandardErrorReader != null) StandardErrorReader.Abort(); } catch { }
            StandardOutputReader = null;
            StandardErrorReader = null;
        }
    }
}
3 голосов
/ 03 января 2011

Это не отвечает на вопрос, но метод Registry.OpenRemoteBaseKey подключается к реестру другого компьютера так же, как и команда REG. Вызовите RegistryKey.GetSubKeyNames, чтобы получить тот же вывод, что и REG QUERY.

0 голосов
/ 03 января 2011

Вы можете захватить StandardOutput и StandardError с помощью класса System.Diagnostics.Process.

http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx

Обязательно прочитайте раздел замечаний в документации.Некоторые свойства класса процесса должны быть установлены правильно, чтобы StandardOutput был доступен (например, для UseShellExecute должно быть установлено значение false).

...