簡體   English   中英

如何在C中以編程方式實現“tee”?

[英]How can I implement 'tee' programmatically in C?

我在C中尋找一種以編程方式(即,不使用命令行重定向)實現'tee'功能的方式,以便我的stdout同時轉到stdout和日志文件。 這需要適用於我的代碼和輸出到stdout的所有鏈接庫。 有什么辦法嗎?

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

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()返回FILE* ,但dup2()需要文件描述符(fd),因此fileno()FILE*轉換為fd。 然后dup2(..., STDOUT_FILENO)表示用popen()的fd替換stdout。

這意味着,您生成了一個子進程( popen ),它將所有輸入復制到stdout和一個文件,然后將stdout移植到該進程。

您可以將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);

您可以使用pipe(2)dup2(2)將標准輸出連接到可以讀取的文件描述符。 然后你可以有一個單獨的線程監視該文件描述符,將它獲取的所有內容寫入日志文件和原始標准輸出(在連接管道之前由dup2保存到另一個文件描述符)。 但是你需要一個后台線程。

實際上,我認為vatine建議的popen tee方法可能更簡單,更安全(只要您不需要對日志文件進行額外的任何操作,例如時間戳或編碼等)。

在C中沒有瑣碎的方法。我懷疑最簡單的方法是調用popen(3),將tee作為命令,將所需的日志文件作為arument,然后dup2(2)新打開的文件描述符FILE *到fd 1。

但這看起來有點難看,我必須說我沒試過這個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM