[Python-es] subprocess.popen, capturar stdout :)

Oswaldo Hernández listas en soft-com.es
Vie Nov 2 15:29:54 CET 2007


Oswaldo Hernández escribió:
> Rafael Villar Burke escribió:
> 
>> Si no es muy extensa (me imagino que no), incluso podría ir como adjunto
>> a la lista, para que quede en los archivos.
>> Seguro que a mucha gente le interesa.
>>
> 
> No se si la lista permite archivos adjuntos, si no llega envio otro 
> mensaje con codigo en el cuerpo del mensaje.
> 

No ha llegado, aqui va el codigo:

/**********************************************************
pipe.c

02-11-07
Oswaldo Hernández
oswaldo en soft-com.es

Captura stdout y stderr de la aplicación a ejecutar y los envia
por lineas a la salida std añadiendo el prefijo 'Out:' o 'Err:'
dependiendo de donde se haya capturado.
Despues del envio de cada linea realiza un flush para asegurar
que no quede en el buffer.

Compilación:
     gcc pipe.c -o pipe

Uso:
     pipe aplicacion parametro parametro ...

TODO:
     Añadir parametros para configurar/ampliar funcionalidad:
     /NOPRE  -> no enviar prefijos con los mensajes
     /IN     -> crear pipe para reenviar stdin a la aplicacion
                 y establecer comunicacion bidireccional
***********************************************************/

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

#define BUFZISE 1024
typedef struct {
     char buf[BUFZISE];
     int bytes;
} buffer;

void envialinea(buffer *buf, char *prefijo);

int main(int argc, char** argv)
{
     pid_t pid;
     int rv;
     int outpipe[2];
     int errpipe[2];

     if (argc <= 1)  {
         printf("Parametros insuficientes, debe especificar la aplicacion a ejecutar.\n");
         exit(1);
     }

     /* Setup communication pipeline first */
     if(pipe(outpipe) || pipe(errpipe)){
         fprintf(stderr,"Pipe error!\n");
         exit(1);
     }
     /* Attempt to fork and check for errors */
     if( (pid=fork()) == -1){
         fprintf(stderr,"Fork error. Exiting.\n");  /* something went wrong */
         exit(1);
     }
     if(pid){
         int retval;
         buffer bout;
         buffer berr;
         bout.bytes = 0;
         berr.bytes = 0;

         close(outpipe[1]);
         close(errpipe[1]);

         // lecturas no bloqueantes
         fcntl(outpipe[0], F_SETFL, O_NONBLOCK);
         fcntl(errpipe[0], F_SETFL, O_NONBLOCK);
         // buffer de salida
         setvbuf(stdout,(char*)NULL,_IONBF,0);

         // bucle de lecturar hasta que finaliza la aplicacion
         while(1)    {
             retval = read(outpipe[0], bout.buf+bout.bytes, BUFZISE-bout.bytes);
             if (retval > 0) {
                 bout.bytes += retval;
                 envialinea(&bout,"Out");
             }
             retval = read(errpipe[0], berr.buf+berr.bytes, BUFZISE-berr.bytes);
             if (retval > 0) {
                 berr.bytes += retval;
                 envialinea(&berr,"Err");
             }
             if (waitpid(pid, &rv, WNOHANG) != 0)
                 break;
         }
         // salida con el codigo de retorno de la aplicacion
         exit(rv);
     }
     else{
         char cmd[512];
         int x;
         // stdout y stderr sin buffer
         setvbuf(stdout,(char*)NULL,_IONBF,0);
         setvbuf(stderr,(char*)NULL,_IONBF,0);

         // asignar stdout y stderr a los pipes
         dup2(outpipe[1],1);
         dup2(errpipe[1],2);
         close(outpipe[0]);
         close(errpipe[0]);

         // ejecutar aplicacion
         cmd[0] = 0;
         for (x = 0; x < argc; x++)  {
             strcat(cmd, argv[x]);
             strcat(cmd, " ");
         }
         // OjO, el primer parametro de argv soy yo
         argv ++;
         execvp(argv[0], argv);
         // Error de ejecucion
         fprintf(stderr,"execl Error! %d", errno);
         exit(1);

     }
     return 0;
}

/*****************************************
analisis del buffer de entrada,
busca '\n', si lo encuentra extrae la linea
y la envia, caso contrario espera a que se
complete.
*****************************************/
void envialinea(buffer *buf, char *prefijo)
{
     int x;
     int encontrado;
     // fin de cadena
     buf->buf[buf->bytes] = 0;
     while(1)    {
         encontrado = 0;
         // buscar fin de linea
         for (x = 0; x < buf->bytes; x++)    {
             if (buf->buf[x] == '\n')    {
                 encontrado = x;
                 break;
             }
         }
         if (encontrado) {
             // enviar linea
             buf->buf[encontrado] = 0;
             printf("%s:%s\n", prefijo, buf->buf);
             fflush(stdout);
             // desplazar el resto al inicio del buffer
             encontrado ++;
             strcpy(buf->buf, buf->buf + encontrado);
             buf->bytes -= encontrado;
         }
         else
             break; // no hay lineas en el buffer
     }
}


-- 
*****************************************
Oswaldo Hernández
oswaldo (@) soft-com (.) es
*****************************************


Más información sobre la lista de distribución Python-es