如何在C中以编程方式实现“tee”?

我在C中寻找一种以编程方式(即,不使用命令行重定向)实现’tee’function的方式,以便我的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。

但这看起来有点难看,我必须说我没试过这个。