2010-11-27 26 views
202

Me gustaría ejecutar un programa de línea de comando externo desde mi aplicación Mono/.NET. Por ejemplo, me gustaría ejecutar mencoder. ¿Es posible:Process.start: cómo obtener la salida?

  1. Para obtener la salida del shell de la línea de comandos, y escribirla en mi cuadro de texto?
  2. ¿Para obtener el valor numérico para mostrar una barra de progreso con el tiempo transcurrido?

Respuesta

307

Cuando se crea el objeto Process ajustado apropiadamente StartInfo:

var proc = new Process { 
    StartInfo = new ProcessStartInfo { 
     FileName = "program.exe", 
     Arguments = "command line arguments to your executable", 
     UseShellExecute = false, 
     RedirectStandardOutput = true, 
     CreateNoWindow = true 
    } 
}; 

a continuación, inicie el proceso y leer de él:

proc.Start(); 
while (!proc.StandardOutput.EndOfStream) { 
    string line = proc.StandardOutput.ReadLine(); 
    // do something with line 
} 

Puede usar int.Parse() o int.TryParse() para convertir las cadenas en valores numéricos. Es posible que tenga que hacer alguna manipulación de cadenas primero si hay caracteres numéricos no válidos en las cadenas que lee.

+3

me preguntaba cómo se puede tratar con StandardError?. Por cierto, realmente me gusta este fragmento de código! bonito y limpio. – codea

+6

@codea - leer de 'proc.StandardError'. – Ferruccio

+2

Gracias, pero creo que no estaba claro: ¿debería agregar otro ciclo para hacerlo? – codea

6

La manera estándar de .NET de hacer esto es leer la secuencia de proceso 'StandardOutput. Hay un ejemplo en los documentos MSDN enlazados. De manera similar, puede leer desde StandardError, y escribir al StandardInput.

145

Puede procesar su salida sincrónicamente o asincrónicamente.

1: ejemplo síncrono

static void runCommand() { 
    Process process = new Process(); 
    process.StartInfo.FileName = "cmd.exe"; 
    process.StartInfo.Arguments = "/c DIR"; // Note the /c command (*) 
    process.StartInfo.UseShellExecute = false; 
    process.StartInfo.RedirectStandardOutput = true; 
    process.StartInfo.RedirectStandardError = true; 
    process.Start(); 
    //* Read the output (or the error) 
    string output = process.StandardOutput.ReadToEnd(); 
    Console.WriteLine(output); 
    string err = process.StandardError.ReadToEnd(); 
    Console.WriteLine(err); 
    process.WaitForExit(); 
} 

Nota que es mejor para procesar ambos de salida y errores: deben ser manipulados por separado.

(*) Para algunos comandos (en este caso) StartInfo.Arguments debe agregar el /cdirective, de lo contrario el proceso se congela en el WaitForExit().

2: ejemplo asíncrono

static void runCommand() { 
    //* Create your Process 
    Process process = new Process(); 
    process.StartInfo.FileName = "cmd.exe"; 
    process.StartInfo.Arguments = "/c DIR"; 
    process.StartInfo.UseShellExecute = false; 
    process.StartInfo.RedirectStandardOutput = true; 
    process.StartInfo.RedirectStandardError = true; 
    //* Set your output and error (asynchronous) handlers 
    process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); 
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler); 
    //* Start process and handlers 
    process.Start(); 
    process.BeginOutputReadLine(); 
    process.BeginErrorReadLine(); 
    process.WaitForExit(); 
} 
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { 
    //* Do your stuff with the output (write to console/log/StringBuilder) 
    Console.WriteLine(outLine.Data); 
} 

Si no necesita hacer complicar las operaciones con la salida, se puede pasar por alto el método OutputHandler, simplemente añadiendo los controladores de línea directa:

//* Set your output and error (asynchronous) handlers 
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data); 
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data); 
+1

gotta love async! Pude usar este código (con un poco de transcripción) en VB.net –

+0

note 'string output = process.StandardOutput.ReadToEnd();' puede producir una cadena grande si hay muchas líneas de salida; el ejemplo asíncrono y la respuesta de Ferruccio procesan la salida línea por línea. –

+3

** Nota: ** ¡su primer enfoque (síncrono) no es correcto! ¡NO debe leer StandardOutput y StandardError sincrónicamente! causará cerrojos muertos. al menos uno de ellos debe ser asincrónico. –

4

puede utilizar la memoria compartida para que los 2 procesos se comuniquen, consulte MemoryMappedFile

creará principalmente un archivo mapeado de memoria mmf en el proceso principal usando la instrucción "using" y luego creará el segundo proceso hasta que finalice y dejará que escriba el resultado en mmf usando BinaryWriter, luego lea el resultado del mmf usando el padre proceso, también puede pasar el nombre mmf utilizando argumentos de línea de comandos o codificarlo con código.

asegúrese de que cuando se utiliza el archivo asignado en el proceso principal que realice el proceso hijo que escriba el resultado al archivo asignado antes de que el archivo de mapeado se libera en el proceso padre

Ejemplo: proceso de padres

private static void Main(string[] args) 
    { 
     using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("memfile", 128)) 
     { 
      using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 
      { 
       BinaryWriter writer = new BinaryWriter(stream); 
       writer.Write(512); 
      } 

      Console.WriteLine("Starting the child process"); 
      // Command line args are separated by a space 
      Process p = Process.Start("ChildProcess.exe", "memfile"); 

      Console.WriteLine("Waiting child to die"); 

      p.WaitForExit(); 
      Console.WriteLine("Child died"); 

      using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 
      { 
       BinaryReader reader = new BinaryReader(stream); 
       Console.WriteLine("Result:" + reader.ReadInt32()); 
      } 
     } 
     Console.WriteLine("Press any key to continue..."); 
     Console.ReadKey(); 
    } 

proceso niño

private static void Main(string[] args) 
    { 
     Console.WriteLine("Child process started"); 
     string mmfName = args[0]; 

     using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(mmfName)) 
     { 
      int readValue; 
      using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 
      { 
       BinaryReader reader = new BinaryReader(stream); 
       Console.WriteLine("child reading: " + (readValue = reader.ReadInt32())); 
      } 
      using (MemoryMappedViewStream input = mmf.CreateViewStream()) 
      { 
       BinaryWriter writer = new BinaryWriter(input); 
       writer.Write(readValue * 2); 
      } 
     } 

     Console.WriteLine("Press any key to continue..."); 
     Console.ReadKey(); 
    } 

para utilizar este ejemplo, tendrá que crear una solución con 2 proyectos en lado, entonces se toma el resultado de la acumulación del proceso hijo de% childDir%/bin/debug y copiarlo% ParentDirectory%/bin/debug continuación, ejecutar el proyecto principal

childDir y parentDirectory son los nombres de las carpetas de sus proyectos en la pc buena suerte :)

+0

¡Es increíble, nunca he usado eso! – Pipe

6

Muy bien, para cualquier persona que quiera que se lean los errores y las salidas, pero se bloquea con cualquiera de las soluciones, proporcionadas en otras respuestas (como yo), aquí hay una solución que construí después leyendo la explicación de MSDN para la propiedad StandardOutput.

respuesta se basa en el código del T30:

static void runCommand() { 
    //* Create your Process 
    Process process = new Process(); 
    process.StartInfo.FileName = "cmd.exe"; 
    process.StartInfo.Arguments = "/c DIR"; 
    process.StartInfo.UseShellExecute = false; 
    process.StartInfo.RedirectStandardOutput = true; 
    process.StartInfo.RedirectStandardError = true; 
    //* Set ONLY ONE handler here. 
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler); 
    //* Start process 
    process.Start(); 
    //* Read one element asynchronously 
    process.BeginErrorReadLine(); 
    //* Read the other one synchronously 
    string output = process.StandardOutput.ReadToEnd(); 
    Console.WriteLine(output); 
    process.WaitForExit(); 
} 
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { 
    //* Do your stuff with the output (write to console/log/StringBuilder) 
    Console.WriteLine(outLine.Data); 
} 
+0

Gracias por agregar esto. ¿Puedo preguntar qué comando estabas usando? – T30

+0

¿Comando por qué? ¿Que estas preguntando? – cubrman

+0

Estoy desarrollando una aplicación en C# que está diseñada para iniciar mysqldump.exe, mostrar al usuario cada mensaje que genera la aplicación, esperar a que termine y luego realizar algunas tareas más. No puedo entender de qué tipo de comando estás hablando? Toda esta pregunta se trata de lanzar un proceso desde C#. – cubrman

Cuestiones relacionadas