有时,我们需要将标准输出(stdout),标准错误输出(stderr)重定向到文件中,方便我们追踪程序运行信息。这在守护进程(daemon)中非常重要,因为这类进程与控制台脱离关系,所以它们的标准输出都无法在控制台显示。这样只能通过重定向到文件的方法记录标准输出信息。
那么如何才能将标准输出重定向呢?这与unix的文件句柄有关。标准输入、标准输出、标准错误输出对应的文件句柄分别为0、1、2,这些句柄在进程启动时就默认存在的。此后每打开一个文件,文件的句柄就取尚未被占用的最小值。例如,目前只有0、1、2这三个句柄,那么你打开一个文件,这个文件的句柄就是3,再打开一个,就是4。如果现在关闭句柄3,目前存在的句柄就为0、1、2、4。此时你再打开一个文件,那么该文件的句柄就是3,而不是5。了解呼!
利用unix文件句柄的生成特点,我们来完成重定向操作如下:
1. 首先新建文件out.txt,作为我们重定向的文件。然后打开这个文件int old_fd = open("out.txt",O_WRONLY | O_CREAT | O_APPEND,0644); 这样我们就获得了操作这个文件的文件句柄,假设为3。
2. 关闭标准输出句柄close(STDOUT_FILENO); 这为我们下一步复制上面的文件句柄,创造条件。
3. 利用unix的系统调用dup函数,复制文件句柄,并返回新的文件句柄。int new_fd = dup(old_fd); 根据unix文件句柄的生成规则(取尚未占用的最小句柄),这个new_fd的值应为1(STDOUT_FILENO);
4. 最后,当然不要忘了关闭old_fd,因为这个文件句柄已经没有用了。close(old_fd),释放系统资源。
我们将上面的操作转化为代码并测试如下:
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#define err_exit(m) {perror(m); exit(1);}
void main(){
int old_fd, new_fd;
printf("Before Redirect: Hello, World\n");
old_fd = open("out.txt", O_WRONLY | O_CREAT | O_APPEND , 0644);
if(old_fd == -1) err_exit("open error");
close(STDOUT_FILENO);//关闭标准输出句柄;
new_fd = dup(old_fd);//复制old_fd文件句柄,并返回新的文件句柄;
if(new_fd == -1) err_exit("dup error");
close(old_fd); //关闭old_fd,释放系统资源;
printf("After Redirect: Hello, World\n");
}
当然linux也提供了更为方便的系统调用dup2(old_fd, new_fd);它会先将new_fd关闭,然后复制old_fd,最后自动关闭old_fd,这样简化了我们操作,如下所示:
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#define err_exit(m) {perror(m); exit(1);}
void main(){
int old_fd, new_fd;
int retVal;
printf("Before Redirect: Hello, World\n");
old_fd = open("out.txt", O_WRONLY | O_CREAT | O_APPEND , 0644);
if(old_fd == -1) err_exit("open error");
retVal = dup2(old_fd, STDOUT_FILENO);
if(retVal == -1) err_exit("dup2 error");
printf("After Redirect: Hello, World\n");
}
我们查看一下结果:cat out.txt
最后,祝贺你完成了输入输出重定向的功能!!