目录
1.重定向
1.1引入


首先使用close接口关闭文件描述符为1的文件(起初是标准输出文件)
然后open函数打开一个文件,根据最小未使用文件描述符策略,该文件的文件描述符为1
接着向文件描述符为1的文件写入5条信息
最后再关闭open函数打开的文件
这样就实现了输出重定向的非常搓的版本(先关闭再打开):原本向标准输出文件写入的内容变为了写入filename文件当中
1.2dup2
实现输入输出追加重定向,调用dup2函数即可,这样就不用先关闭再打开了
| 类别 | 详情 |
|---|---|
| 函数原型 | #include <unistd.h> int dup2(int oldfd, int newfd); |
| 功能描述 | 将文件描述符 oldfd 复制到 newfd,使两者指向同一文件表项。若 newfd 已打开,则先关闭它。 |
| 参数说明 | - oldfd:源文件描述符(必须有效且已打开)。 - newfd:目标文件描述符(可指定,范围通常为 [0, OPEN_MAX-1])。 |
| 返回值 | - 成功:返回 newfd。 - 失败:返回 -1,并设置 errno(如 EBADF、EMFILE)。 |
(1)输出重定向

(2)追加重定向

(3)输入重定向

1.3原理
文件描述符实质上是进程文件描述符表fd_array[]的数组下标,表中存储的是指向内核文件对象(struct file)的指针。dup2()的功能表面上是指定文件描述符的复制,实质上是将一个文件描述符表项中的指针复制到另一个表项中。重定向的原理就是:进程通过修改文件描述符表项中的指针指向,使得对特定文件描述符的操作实际作用于另一个文件。

1.4重定向的实现
实现重定向的思路是:在执行需要重定向的操作之前,通过系统调用(如dup2())让内核修改进程的文件描述符表,使得特定的文件描述符指向不同的文件对象,从而永久改变后续所有通过该文件描述符进行的I/O操作的目标
void normalExecute(char* argv[])
{
//创建子进程执行普通命令
pid_t id = fork();
if(id == 0)
{
//打开重定向文件,修改文件描述符的对应内容
//文件描述符
int fd = 0;
//输入重定向
if(rdir == IN_RDIR)
{
fd = open(rdirfilename, O_RDONLY);
dup2(fd, 0);
}
//输出重定向
else if(rdir == OUT_RDIR)
{
fd = open(rdirfilename, O_CREAT|O_WRONLY|O_TRUNC, 0666);
dup2(fd, 1);
}
//追加重定向
else if(rdir == APPEND_RDIR)
{
fd = open(rdirfilename, O_CREAT|O_WRONLY|O_APPEND, 0666);
dup2(fd, 1);
}
//子进程执行普通命令
//参数1程序名,参数2命令行参数数组
execvp(argv[0], argv);
exit(EXIT_FAILURE);//替换失败返回错误码
}
else if(id > 0)
{
//父进程阻塞等待
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if(rid > 0)//等待成功
{ }
else//等待失败
{ }
}
else
{
perror("fork fail\n");
exit(EXIT_FAILURE);//创建子进程败
}
}
1.5重定向与进程替换

结论:程序替换不会影响文件访问(包括重定向)
程序替换(exec)不会改变进程已打开的文件状态,包括重定向设置。这是因为Linux内核将进程管理与文件管理解耦,文件描述符表作为进程的一部分在exec时被保留。通过系统调用(如dup2)修改的文件描述符关联关系会被新程序继承,从而实现重定向的持久效果
2.2重定向示例
-
仅重定向标准输出
./mytest > log.txt 等价于 ./mytest 1> log.txt
效果:fd=1(标准输出) → log.txt文件 -
分别重定向
./mytest 1>normal.log 2>err.log
效果:
fd=1 → normal.log(正常输出)
fd=2 → err.log(错误输出) -
合并重定向
方式1:
./mytest >all.log 2>&1 等价于 ./mytest 1>all.log 2>&1
执行顺序:
(1) >all.log 或 1>all.log → fd=1指向all.log
(2) 2>&1 → fd=2也指向fd=1当前指向的文件(all.log)
方式2:
./mytest &>all.log 等价于 >all.log 2>&1
错误的方式:
./mytest 1>all.log 2>all.log //error 竞争
!!两个描述符独立打开文件,会产生竞争条件,内容可能交错混乱!!./mytest 2>&1 >all.log //error 顺序错误
2>&1 → fd=2指向fd=1当前指向的文件(此时fd=1仍指向显示器)all.log → fd=1改为指向all.log
结果: fd=2指向显示器,fd=1指向all.log -
> 默认重定向fd=1,2> 重定向fd=2
-
2>&1 不是重定向到文件,而是让fd=2指向fd=1的当前位置
-
顺序很重要:先重定向目标,再复制描述符
-
&> 是bash的简写,等价于>file 2>&1
2.文件描述符1与2
-
文件描述符1(fd=1):标准输出(stdout) - 正常输出信息
-
文件描述符2(fd=2):标准错误(stderr) - 错误信息输出
-
默认情况下,程序刚启动时,文件描述符1和2指向不同的文件对象,但这两个文件对象最终都连接到同一个终端设备
3.一切皆文件的理解

自底向上,Linux内核为统一管理各类硬件设备和系统资源,为每种设备类型定义一组操作方法(如读、写、控制等),封装在file_operations结构体中。当设备被访问时,内核创建struct file对象来描述这次打开操作,其中的f_op指针指向该设备对应的操作方法集。进程通过文件描述符访问文件时,内核查找对应的struct file对象,并通过其f_op指针调用具体的设备操作方法。这种抽象层在用户空间表现为统一的文件接口,从而实现了"一切皆文件"的设计哲学