2011-01-03 13 views
16

Resumen:Cómo capturar el resultado del comando Shell en C#?

  • registro de consulta en la máquina remota
  • salida
  • captura para su uso en aplicaciones
  • tiene que estar en CSharp
  • hasta ahora todos los métodos utilizados sólo pueden consultar en la máquina local
  • cualquier esperanza es muy apreciada

Número completo:

Necesito encontrar una manera de ejecutar un comando de línea de comando en csharp y capturar su salida. Sé cómo hacer esto en Perl, a continuación se muestra el código que usaría en 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`; 

Cualquier sugerencia sobre cómo hacer esto en csharp sería bienvenida. Hasta ahora he intentado usar el método RegistryKey OurKey = Registry.Users y funciona muy bien, pero no puedo consultar el registro en una máquina remota.

Háganme saber si necesita más información.

SOLUCIÓN: (Gracias a @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"); 


     } 
+0

posible duplicado de [Captura de salida de la shell nslookup con C#] (http://stackoverflow.com/questions/353601/capturing-nslookup-shell-output-with-c) –

+2

¿Usted intentó 'RegistryKey.OpenRemoteBaseKey ¿? http://msdn.microsoft.com/en-us/library/8zha3xws.aspx –

+0

PowerShell sería una opción mucho mejor. – TrueWill

Respuesta

27

Es posible que tenga que ajustar esto un poco, pero aquí hay algunos (ligeramente modificada de la original) de código que redirige stdout y stderr de un proceso:

 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(); 

     } 
+0

Gracias por el código. Solo traté de implementarlo y ejecutarlo. Estoy teniendo problemas para obtener el resultado. Actualmente estoy usando Output.AppendText (output + "\ n"); para imprimir la salida con. ¿es esto correcto? Soy nuevo en csharp (aproximadamente 3 horas de experiencia total :)) – toosweetnitemare

+0

Esta es mi solución. Simplemente tuve que lanzar el nombre de la máquina en la variable :). ¡Muchas gracias! – toosweetnitemare

+0

Tenga en cuenta que este código * debe * funcionar para 'reg.exe', pero se bloqueará con un interbloqueo para un programa que escribe lo suficiente en su secuencia de error estándar para llenar el tamaño de búfer predeterminado. La solución correcta para el caso general es leer ambos flujos de salida al mismo tiempo, con hilos separados. –

3

Esto no responde a la pregunta, pero el método Registry.OpenRemoteBaseKey conecta con el registro de otra máquina de la misma forma que el comando REG hace. Llame al RegistryKey.GetSubKeyNames para obtener el mismo resultado que REG QUERY.

+0

voy a intentarlo ahora mismo. ¡Gracias! – toosweetnitemare

0

Puede capturar StandardOutput y StandardError utilizando la clase System.Diagnostics.Process.

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

Asegúrese de leer la sección de observaciones de la documentación. Ciertas propiedades de la clase de proceso deben configurarse correctamente para que StandardOutput esté disponible (por ejemplo, UseShellExecute debe establecerse en falso).

8

Prácticamente cualquier cosa que pueda ejecutar en la línea de comandos se puede ejecutar en un programa C# con restricciones similares. Hay algunas formas de hacerlo, una a través de los comandos de proceso asíncrono que muestro en mi blog. Simplemente escribe y lee en la línea de comando de forma activa. A partir de aquí, solo averigüe qué quiere lograr y cómo hacerlo con una línea de comando. A continuación, conectarlo en el programa

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); 
} 
} 

Como se puede ver, sólo tiene una instancia de la clase, controla el evento de salida, y empezar a escribir comandos como si estuvieras Typeing en el símbolo del sistema.

Así es como funciona:

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; } 
} 
+0

Muchas gracias por el código. Me tomará unos minutos probar una aplicación para mi aplicación. – toosweetnitemare

4

Aquí hay una clase que uso. Está adaptado del código que encontré en un blog posting hace un tiempo, pero con varias otras modificaciones.

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 ([email protected]) 
     * 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; 
     } 
    } 
} 
+2

He adaptado su código y he creado una biblioteca de clases que [he puesto en GitHub] (https://github.com/kenny-evitt/ExecuteCommandLineProgram). ¿Algún problema con eso? –

+1

@KennyEvitt: No hay problema por mi parte. Hasta donde yo sé, el código es mío (excepto las partes que provienen del comentarista "SeemabK" en el blog de Scott Hanselman, por supuesto). Ya no trabajo para el empleador para el que trabajaba cuando escribí eso, pero tampoco creo que lo reclamen. Entonces creo que estás bien. –

Cuestiones relacionadas