Подтвердить что ты не робот

Как я могу реализовать "тройник" программно в C?

Я ищу способ на C программно (т.е. не используя перенаправление из командной строки) реализую функцию "tee", так что мой stdout переходит в оба файла stdout и log. Это должно работать как для моего кода, так и для всех связанных библиотек, которые выводятся в stdout. Любой способ сделать это?

4b9b3361

Ответ 1

Вы можете popen() программу tee.

Или вы можете fork() и pipe stdout через дочерний процесс, такой как этот (адаптированный из реальной живой программы, которую я написал, поэтому она работает!):

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

Ответ 2

Ответы "popen() tee" были правильными. Вот пример программы, которая делает именно это:

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

Пояснение:

popen() возвращает a FILE*, но dup2() ожидает файловый дескриптор (fd), поэтому fileno() преобразует FILE* в fd. Затем dup2(..., STDOUT_FILENO) говорит, чтобы заменить stdout на fd из popen().

Смысл: вы создаете дочерний процесс (popen), который копирует все его входные данные в stdout и файл, затем вы переносите свой stdout в этот процесс.

Ответ 3

Вы можете использовать forkpty() с exec() для выполнения контролируемой программы с ее параметрами. forkpty() возвращает дескриптор файла, который перенаправляется на программы stdin и stdout. Все, что написано в дескрипторе файла, является входом программы. Все, что написано программой, можно прочитать из дескриптора файла.

Вторая часть состоит в том, чтобы читать в цикле вывод программы и записывать ее в файл, а также печатать его на stdout.

Пример:

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

Ответ 4

Вы можете использовать pipe(2) и dup2(2), чтобы подключить ваш стандарт к файловому дескриптору, с которого вы можете читать. Затем вы можете иметь отдельный поток, контролирующий этот файловый дескриптор, записывая все, что попадает в файл журнала, и исходный stdout (сохраняемый avay на другой filedescriptor на dup2 перед подключением к каналу). Но вам понадобится фоновый поток.

На самом деле, я думаю, что метод popen tee, предложенный vatine, вероятно, проще и безопаснее (до тех пор, пока вам не нужно делать ничего лишнего с файлом журнала, например, timestamping или encoding или что-то еще).

Ответ 5

Нет никакого тривиального способа сделать это в C. Я подозреваю, что самым простым было бы назвать popen (3), с ти в качестве команды и желаемого файла журнала в качестве arument, затем dup2 (2) файловый дескриптор недавно открытый FILE * на fd 1.

Но это выглядит некрасиво, и я должен сказать, что я НЕ пробовал это.