2009-11-19 8 views
12

Estoy buscando un camino en C para programar (es decir, no usar la redirección de la línea de comandos) implementar la funcionalidad 'tee' de modo que mi stdout vaya tanto a stdout como a un archivo de registro. Esto debe funcionar tanto para mi código como para todas las bibliotecas vinculadas que dan salida a stdout. Alguna forma de hacer esto?¿Cómo puedo implementar 'tee' programáticamente en C?

+0

¿Puedo pedir un detalle más específico de por qué necesita una solución de este tipo? ¿Es para ahorrar en la depuración ...? – lorenzog

Respuesta

7

Puede popen() el programa de tee.

O puede fork() y el tubo stdout a través de un proceso hijo como este (adaptado de un verdadero programa en vivo que escribí, por lo que funciona!):

void tee(const char* fname) { 
    int pipe_fd[2]; 
    check(pipe(pipe_fd)); 
    const pid_t pid = fork(); 
    check(pid); 
    if(!pid) { // our log child 
     close(pipe_fd[1]); // Close unused write end 
     FILE* logFile = fname? fopen(fname,"a"): NULL; 
     if(fname && !logFile) 
      fprintf(stderr,"cannot open log file \"%s\": %d (%s)\n",fname,errno,strerror(errno)); 
     char ch; 
     while(read(pipe_fd[0],&ch,1) > 0) { 
      //### any timestamp logic or whatever here 
      putchar(ch); 
      if(logFile) 
       fputc(ch,logFile); 
      if('\n'==ch) { 
       fflush(stdout); 
       if(logFile) 
        fflush(logFile); 
      } 
     } 
     putchar('\n'); 
     close(pipe_fd[0]); 
     if(logFile) 
      fclose(logFile); 
     exit(EXIT_SUCCESS); 
    } else { 
     close(pipe_fd[0]); // Close unused read end 
     // redirect stdout and stderr 
     dup2(pipe_fd[1],STDOUT_FILENO); 
     dup2(pipe_fd[1],STDERR_FILENO); 
     close(pipe_fd[1]); 
    } 
} 
+0

+1 por no asumir nada (por ejemplo, el código anterior es fácilmente un adaptador para tomar un descriptor de archivo ya abierto en lugar de un nombre de archivo como argumento) – Michael

+0

tee ("tee.txt "); sistema (" ftp "); -> no parece imprimir stdin o stdout alguna idea? – Victor

+0

una vez que quité: if ('\ n' == ch) se imprime. Supongo que estaba esperando \ n. Ahora la función T parece estar atascada a menos que presione enter al final. – Victor

-1

No hay manera trivial de hacer esto en C Sospecho que lo más fácil sería llamar a popen (3), con tee como el comando y el archivo de registro deseado como un arument, luego dup2 (2) el descriptor de archivo del FILE * recién abierto en fd 1.

Pero eso se ve feo y debo decir que NO lo he intentado.

+0

Probé esto y causa condiciones de carrera. Un problema puede ser que el tee se imprima primero a veces. – PALEN

+0

@PALEN Como dije, "no probado y se ve feo". Si está utilizando GNU libc, hay una funcionalidad para crear sus propios objetos similares a FILE * y podría usar eso, pero luego terminaría en unicability-land. – Vatine

1

Puede usar forkpty() con exec() para ejecutar el programa supervisado con sus parámetros. forkpty() devuelve un descriptor de archivo que se redirige a los programas stdin y stdout. Lo que está escrito en el descriptor de archivo es la entrada del programa. Lo que está escrito por el programa se puede leer desde el descriptor de archivo.

La segunda parte es leer en un bucle la salida del programa y escribirlo en un archivo y también imprimirlo en stdout.

Ejemplo:

pid = forkpty(&fd, NULL, NULL, NULL); 
if (pid<0) 
    return -1; 

if (!pid) /* Child */ 
{ 
execl("/bin/ping", "/bin/ping", "-c", "1", "-W", "1", "192.168.3.19", NULL); 
} 

/* Parent */ 
waitpid(pid, &status, 0); 
return WEXITSTATUS(status); 
+0

Pero esto significa que ejecutar su programa desde otro programa no lo hace Presumiblemente, él quiere evitar esto, de lo contrario estaría dispuesto a usar tee. – Benj

1

Usted podría utilizar pipe(2) y dup2(2) para conectar la salida estándar a un descriptor de archivo se puede leer. Luego puede tener un hilo separado que monitorea ese descriptor de archivo, escribiendo todo lo que obtiene en un archivo de registro y el stdout original (aval guardado en otro descriptor de archivo por dup2 antes de conectar el conducto). Pero necesitarías un hilo de fondo.

En realidad, creo que el método popen tee sugerido por vatine es probablemente más simple y seguro (siempre que no necesite hacer nada adicional con el archivo de registro, como la marca de tiempo o codificación o algo así).

5

Las respuestas "popen() tee" eran correctas. He aquí un ejemplo de programa que hace exactamente eso:

#include "stdio.h" 
#include "unistd.h" 

int main (int argc, const char * argv[]) 
{ 
    printf("pre-tee\n"); 

    if(dup2(fileno(popen("tee out.txt", "w")), STDOUT_FILENO) < 0) { 
     fprintf(stderr, "couldn't redirect output\n"); 
     return 1; 
    } 

    printf("post-tee\n"); 

    return 0; 
} 

Explicación:

popen() devuelve un FILE*, pero dup2() espera un descriptor de archivo (fd), por lo que convierte la fileno()FILE* a un fd. Luego, dup2(..., STDOUT_FILENO) dice que se reemplace la salida estándar con la fd desde popen().

Es decir, genera un proceso secundario (popen) que copia toda su entrada en stdout y un archivo, luego transfiere su stdout a ese proceso.

Cuestiones relacionadas