我在C中寻找一种以编程方式(即,不使用命令行重定向)的方法实现'tee'功能,以便我的stdout同时转到stdout和日志文件。这需要适用于我的代码和输出到stdout的所有链接库。有什么办法吗?
答案 0 :(得分:7)
你可以popen()
开球项目。
或者你可以通过这样的子进程fork()
和管道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]);
}
}
答案 1 :(得分:6)
“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移植到该进程。
答案 2 :(得分:1)
您可以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);
答案 3 :(得分:1)
您可以使用pipe(2)
和dup2(2)
将标准输出连接到您可以读取的文件描述符。然后你可以有一个单独的线程监视该文件描述符,将它获取的所有内容写入日志文件和原始标准输出(在连接管道之前由dup2
保存到另一个文件描述符)。但是你需要一个后台线程。
实际上,我认为vatine建议的popen tee方法可能更简单,更安全(只要您不需要对日志文件进行额外的任何操作,例如时间戳或编码等)。
答案 4 :(得分:-1)
在C中执行此操作并不是一件轻而易举的事。我怀疑最简单的方法是调用popen(3),将tee作为命令,将所需的日志文件作为arument,然后dup2(2)调用文件描述符。新打开的FILE *到fd 1。
但这看起来有点难看,我必须说我没试过这个。