¿Cómo puedo implementar ‘tee’ programáticamente en C?

5 minutos de lectura

Estoy buscando una manera en C para implementar programáticamente (es decir, sin usar la redirección desde la línea de comando) la funcionalidad ‘tee’ de modo que mi salida estándar vaya tanto a la salida estándar como a un archivo de registro. Esto debe funcionar tanto para mi código como para todas las bibliotecas vinculadas que generan salida estándar. Alguna forma de hacer esto?

  • ¿Puedo preguntar un detalle más específico de por qué necesita esa solución? ¿Es para ahorrar en la depuración ..?

    – lorenzog

    19 de noviembre de 2009 a las 11:49

¿Como puedo implementar tee programaticamente en C
Voluntad

Tú podrías popen() el programa de té.

O tu puedes fork() y pipa stdout a través de un proceso secundario como este (adaptado de un programa real en vivo que escribí, ¡así 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]);  
    }
}

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

    – Miguel

    22/04/2015 a las 23:31

  • tee(“tee.txt”); sistema (“ftp”); -> no parece imprimir stdin o stdout alguna idea?

    – Víctor

    1 oct 2016 a las 23:33


  • una vez que eliminé: si (‘\ n’ == ch) se imprime. Supongo que estaba esperando \n. Ahora la función tee parece estar atascada a menos que presione enter al final.

    – Víctor

    3 oct 2016 a las 17:22

1647636607 245 ¿Como puedo implementar tee programaticamente en C
NHDaly

Los “popen() tee” las respuestas eran correctas. Aquí hay un programa de ejemplo 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 fileno() convierte el FILE* a un fd. Luego dup2(..., STDOUT_FILENO) dice que reemplace stdout con el fd de popen().

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

podrías usar pipe(2) y dup2(2) para conectar su salida estándar a un descriptor de archivo del que pueda leer. Luego, puede tener un subproceso separado que supervise ese descriptor de archivo, escribiendo todo lo que llegue a un archivo de registro y la salida estándar original (guardada en otro descriptor de archivo por dup2 antes de conectar la tubería). 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 la codificación o algo así).

Puedes usar forkpty() con exec() para ejecutar el programa monitoreado con sus parámetros. forkpty() devuelve un descriptor de archivo que se redirige a los programas stdin y stdout. Lo que se escribe en el descriptor de archivo es la entrada del programa. Todo lo que escribe el programa se puede leer desde el descriptor de archivo.

La segunda parte es leer en un bucle la salida del programa y escribirla en un archivo y también imprimirla en la salida estándar.

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

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

Pero eso se ve un poco feo y debo decir que NO lo he probado.

  • Intenté esto y provoca condiciones de carrera. Un problema puede ser que la camiseta se imprima primero a veces.

    – PALEN

    21 de julio de 2013 a las 21:05

  • @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 un terreno de inportabilidad.

    – Vatina

    23 de julio de 2013 a las 9:35

  • Intenté esto y provoca condiciones de carrera. Un problema puede ser que la camiseta se imprima primero a veces.

    – PALEN

    21 de julio de 2013 a las 21:05

  • @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 un terreno de inportabilidad.

    – Vatina

    23 de julio de 2013 a las 9:35

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad