12 Stimmen

Wie kann ich 'tee' programmatisch in C implementieren?

Ich bin auf der Suche nach einer Möglichkeit, in C programmatisch (dh nicht mit Umleitung von der Kommandozeile) implementieren "T-Stück" Funktionalität, so dass meine stdout geht sowohl stdout und eine Protokolldatei. Dies muss sowohl für meinen Code als auch für alle verlinkten Bibliotheken funktionieren, die nach stdout ausgeben. Gibt es eine Möglichkeit, dies zu tun?

7voto

Will Punkte 71452

Sie könnten popen() das Abschlagsprogramm.

Oder Sie können fork() und Rohr stdout durch einen untergeordneten Prozess wie diesen (angepasst an ein echtes Programm, das ich geschrieben habe, es funktioniert also!):

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

6voto

NHDaly Punkte 6719

Die " popen() tee" waren die richtigen Antworten. Hier ist ein Beispielprogramm, das genau das tut:

#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;
}

Erläuterung:

popen() gibt eine FILE* しかし dup2() erwartet einen Dateideskriptor (fd), also fileno() wandelt die FILE* zu einer fd. Dann dup2(..., STDOUT_FILENO) sagt, dass stdout durch das fd von popen() .

Das heißt, Sie erzeugen einen Kindprozess ( popen ), der alle seine Eingaben nach stdout und in eine Datei kopiert, dann portiert man sein stdout zu diesem Prozess.

2voto

Rasmus Kaj Punkte 3887

Sie könnten verwenden pipe(2) y dup2(2) um Ihren Standardausgang mit einem Dateideskriptor zu verbinden, aus dem Sie lesen können. Dann können Sie einen separaten Thread haben, der diesen Dateideskriptor überwacht und alles, was er bekommt, in eine Protokolldatei und die ursprüngliche stdout schreibt (gespeichert in einem anderen Dateideskriptor durch dup2 vor dem Anschluss der Leitung). Aber Sie bräuchten ein Hintergrundgewinde.

Ich denke, dass die von Vatine vorgeschlagene Popen-Tee-Methode wahrscheinlich einfacher und sicherer ist (solange man nichts Zusätzliches mit der Protokolldatei machen muss, wie z.B. Zeitstempel oder Kodierung oder so).

1voto

eyalm Punkte 3286

Sie können verwenden forkpty() con exec() um das überwachte Programm mit seinen Parametern auszuführen. forkpty() gibt einen Dateideskriptor zurück, der an die Programme stdin und stdout weitergeleitet wird. Alles, was in den Dateideskriptor geschrieben wird, ist die Eingabe des Programms. Was auch immer vom Programm geschrieben wird, kann aus dem Dateideskriptor gelesen werden.

Der zweite Teil besteht darin, die Ausgabe des Programms in einer Schleife zu lesen und in eine Datei zu schreiben sowie sie auf stdout auszugeben.

Beispiel:

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

-2voto

Vatine Punkte 19955

Es gibt keinen trivialen Weg, dies in C zu tun. Ich vermute, der einfachste wäre, popen(3) aufzurufen, mit tee als Befehl und der gewünschten Protokolldatei als Arument, und dann dup2(2) den Dateideskriptor der neu geöffneten DATEI* auf fd 1.

Aber das sieht irgendwie hässlich aus, und ich muss sagen, dass ich das NICHT ausprobiert habe.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X