2008-09-24 12 views
50

Estoy tratando de iniciar una aplicación externa a través de system() - por ejemplo, system("ls"). Me gustaría capturar su salida a medida que sucede, así puedo enviarla a otra función para su posterior procesamiento. ¿Cuál es la mejor manera de hacerlo en C/C++?Captura de stdout de un comando de sistema() de manera óptima

+0

¿A qué te refieres de manera óptima? De mi respuesta, diría que de manera óptima puede depender de cada situación. El enfoque fork/exec/dup2/STDOUT_FILENO puede ser adecuado para casos especiales. – nephewtom

Respuesta

0

No estoy del todo seguro de que sea posible en el estándar C, ya que dos procesos diferentes normalmente no comparten espacio de memoria. La forma más sencilla en que se me ocurre hacerlo es hacer que el segundo programa redirija su salida a un archivo de texto (nombre de programa> archivo de texto.txt) y luego volver a leer ese archivo de texto para procesarlo. Sin embargo, esa puede no ser la mejor manera.

6

EDITAR: leyó mal la pregunta como querer pasar la salida a otro programa, no a otra función. popen() es casi seguramente lo que quieres.

El sistema le da acceso completo a la carcasa. Si desea continuar usándolo, puede redirigir su salida a un archivo temporal, por sistema ("ls> tempfile.txt"), pero elegir un archivo temporal seguro es un problema. O bien, puede incluso redirigirlo a través de otro programa: system ("ls | otherprogram");

Algunos pueden recomendar el comando popen(). Esto es lo que quiere si se puede procesar la salida de sí mismo:

FILE *output = popen("ls", "r"); 

que le dará un apuntador de archivo se puede leer en la salida del comando en él.

También puede utilizar la tubería() para crear una conexión en combinación con un tenedor() para crear nuevos procesos, dup2() para cambiar la entrada estándar y la salida de ellos, exec() para ejecutar los nuevos programas, y espere() en el programa principal para esperarlos. Esto simplemente está configurando la tubería de manera similar a como lo haría el caparazón. Consulte la página man de pipe() para obtener detalles y un ejemplo.

1

En Windows, en lugar de usar system(), use CreateProcess, redirija la salida a una tubería y conéctela a la tubería.

Supongo que esto también es posible de alguna manera POSIX?

40

Desde el manual popen:

#include <stdio.h> 

FILE *popen(const char *command, const char *type); 

int pclose(FILE *stream); 
+1

ARCHIVO * stream = popen (comando const char *, tipo const char *); – Arpith

+5

Me gustaría agregar que esto solo funcionará en los sistemas de Posix. En mi versión de Windows 7 no hay función popen, pero hay un _popen que funciona. – user1505520

30

Pruebe la función popen(). Ejecuta un comando, como system(), pero dirige el resultado a un nuevo archivo. Se devuelve un puntero a la secuencia.

FILE *lsofFile_p = popen("lsof", "r"); 

    if (!lsofFile_p) 
    { 
    return -1; 
    } 

    char buffer[1024]; 
    char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p); 
    pclose(lsofFile_p); 
1

Las funciones popen() y pclose() podría ser lo que estás buscando.

Eche un vistazo a glibc manual para un ejemplo.

3

Las funciones popen() y tales no redireccionan stderr y tal; Escribí popen3() para ese propósito.

1

En realidad, acabo de comprobar, y:

  1. popen es problemático, ya que el proceso se bifurca. Entonces, si necesita esperar a que se ejecute el comando de shell, entonces está en peligro de perderlo. En mi caso, mi programa se cerró incluso antes de que la tubería hiciera su trabajo.

  2. Terminé usando sistema de llamada con comando tar en Linux. El valor de retorno de sistema fue el resultado de tar.

Así: si necesita el valor de retorno, entonces no hay sólo no hay necesidad de utilizar popen, probablemente no va a hacer lo que quiere.

+1

La pregunta solicita capturar la * salida *, no el * código de salida *. Entonces, no creo que surja el problema que mencionas. –

+1

eso es para lo que la familia wait (...) de llamadas al sistema es para. Esperan a que finalice el proceso de su hijo, para que pueda obtener su resultado. "man -s2 wait" –

1

En esta página: capture_the_output_of_a_child_process_in_c describe las limitaciones del uso de popen frente al enfoque fork/exec/dup2/STDOUT_FILENO.

Tengo problemas capturing tshark output with popen.

y supongo que esta limitación podría ser mi problema:

Devuelve una corriente stdio en contraposición a un descriptor de archivo en bruto, que no es adecuado para el manejo de la salida de forma asíncrona.

Volveré a esta respuesta si tengo una solución con el otro enfoque.

2

La manera más eficaz es utilizar stdout descriptor de archivo directamente, sin pasar por FILE corriente:

pid_t popen2(const char *command, int * infp, int * outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) == -1) 
     return -1; 

    if (pipe(p_stdout) == -1) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     return -1; 
    } 

    pid = fork(); 

    if (pid < 0) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     close(p_stdout[0]); 
     close(p_stdout[1]); 
     return pid; 
    } else if (pid == 0) { 
     close(p_stdin[1]); 
     dup2(p_stdin[0], 0); 
     close(p_stdout[0]); 
     dup2(p_stdout[1], 1); 
     dup2(::open("/dev/null", O_WRONLY), 2); 

     /// Close all other descriptors for the safety sake. 
     for (int i = 3; i < 4096; ++i) { 
      ::close(i); 
     } 

     setsid(); 
     execl("/bin/sh", "sh", "-c", command, NULL); 
     _exit(1); 
    } 

    close(p_stdin[0]); 
    close(p_stdout[1]); 

    if (infp == NULL) { 
     close(p_stdin[1]); 
    } else { 
     *infp = p_stdin[1]; 
    } 

    if (outfp == NULL) { 
     close(p_stdout[0]); 
    } else { 
     *outfp = p_stdout[0]; 
    } 

    return pid; 
} 

Para leer la salida de niño uso popen2() así:

int child_stdout = -1; 
pid_t child_pid = popen2("ls", 0, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 

Para tanto de escritura y léase:

int child_stdin = -1; 
int child_stdout = -1; 
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

const char text = "1\n2\n123\n3"; 
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1); 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 
+1

¿Es correcto dup2 (:: open ("/ dev/null", O_RDONLY), 2) ;? Parece más lógica dup2 (:: open ("/ dev/null", O_WRONLY), 2); –

+0

@MelVisoMartinez, sí, sería más válido. – GreenScape

Cuestiones relacionadas