023 基础 IO —— 重定向

023 基础 IO —— 重定向
小米里的大麦重定向
1. 什么是重定向?
重定向本质上就是操作文件描述符(每一个打开的文件或设备在内核中都有一个编号,称为文件描述符),即 修改标准输入/输出/错误 这三个文件描述符(file descriptor,简称 FD)的指向,让它们 不再指向默认终端(屏幕、键盘),而是指向 文件、设备或者其他地方。
| 文件描述符编号 | 描述 | 默认指向 |
|---|---|---|
| 0 | 标准输入 stdin |
键盘 |
| 1 | 标准输出 stdout |
屏幕 |
| 2 | 标准错误 stderr |
屏幕 |
比如常见的 shell 命令:
1 | ls > output.txt |
背后的本质动作是:
- 关闭文件描述符 1(stdout)
- 打开或创建
output.txt - 将描述符 1 指向
output.txt文件
这样,所有本应该显示在屏幕上的内容,都会写入 output.txt 文件。
2. 常见的重定向符号
| 符号 | 含义 | 功能描述 | 示例 | 效果 |
|---|---|---|---|---|
> |
输出重定向(覆盖),stdout |
把标准输出写入到指定文件,若文件存在则 清空 后写入,否则创建 | ls > out.txt |
将 ls 的 stdout 写入(或覆盖)out.txt |
>> |
输出追加,stdout |
把标准输出追加到指定文件末尾,若文件不存在则创建 | echo hi >> out.txt |
将 hi 追加到 out.txt 末尾 |
< |
输入重定向,stdin |
从指定文件读取内容作为标准输入 | wc -l < in.txt |
将 in.txt 作为 stdin 传给 wc -l |
<< |
Here-Document(多行输入) | 在脚本中内联一段文本作为标准输入,直到遇到结束标识符 |
3. 使用 dup2 系统调用
1. dup2 是什么?
dup2 是一个系统调用,用于复制文件描述符。本质作用就是:让两个文件描述符指向同一个内核打开文件表项。经常用于做输入输出重定向。
2. 函数原型
1 |
|
参数:
oldfd: 已经打开的文件描述符(比如文件、管道等)newfd: 要复制到的新文件描述符(比如 0/1/2)
返回值:成功: 返回 newfd;失败: 返回 -1,并设置 errno 错误号(需要 perror 打印)
3. dup2 做了什么事情?(内部流程)
假设调用 dup2(oldfd, newfd):
- 如果
oldfd == newfd:直接返回newfd,什么也不做(高效优化); - 如果
newfd已经打开,会 先关闭 newfd(防止资源泄漏); - 然后 让 newfd 指向 oldfd 指向的内核文件表项。
注意:
1 | ┌──────────────┐ |
不是简单复制数字,而是让它们“指向同一块内核资源”!dup2 后,oldfd 和 newfd 同时指向同一个内核文件表结构。修改一边,另一边也受影响(因为指的是同一份数据)。
4. 代码实验验证
如何 将 printf 打印内容重定向到一个文件中:
1 |
|
运行示例:
1 | gcc dup2_1.c -o dup2_1 |
你会看到 output.txt 文件里出现了程序输出的内容,而不是显示在屏幕上!
5. 标准错误也能重定向
如果想把错误信息也重定向,可以:
1 | dup2(fd, STDERR_FILENO); // 把标准错误(2)也重定向到 fd |
这样 perror、fprintf(stderr, ...) 的内容也会写进文件!
6. minishell 重定向
1 |
|
4. 1(stdout) VS 2(stderr)
还是那句话,在类 Linux 中,每个进程启动时都会默认打开三个 文件描述符(file descriptor),分别对应三个“数据通道(stream)”:
| 描述符 | 名称(英文) | 默认指向 | 常用符号 |
|---|---|---|---|
| 0 | 标准输入(stdin) | 键盘(或重定向的输入文件) | < |
| 1 | 标准输出(stdout) | 终端屏幕(或重定向的输出文件) | > |
| 2 | 标准错误(stderr) | 终端屏幕(或重定向的错误文件) | 2> |
1. 基本重定向:分开输出
1>file1.txt将 stdout 写入file1.txt2>file2.txt将 stderr 写入file2.txt
1 | ./mytest 1>file1.txt 2>file2.txt # 执行名为 mytest 的程序,并进行输出重定向 |
如果只写 >file1.txt,Shell 会把它当作 1>file1.txt 处理,因为不加数字时默认重定向文件描述符 1(stdout)。
2. 将 stderr 重定向到 stdout 已指向的文件
有时希望把 stderr 合并到与 stdout 相同的目标中,例如:
1 | ./mytest >all.txt 2>&1 |
这里的执行顺序和含义要注意:
>all.txt: 先把 stdout (1) 重定向到all.txt。2>&1: 再把 stderr (2) “重定向到(&)” 描述符 1 当前所指向的地方。
最终,all.txt 会包含正常输出和错误输出,顺序按照程序真正写入时的先后混合在一起。
注意顺序
- 如果写成
./mytest 2>&1 >all.txt,则会先将 stderr 重定向到 原先 的 stdout(通常还是终端),然后再把 stdout 重定向到all.txt,结果是:
- stderr 仍然打印到终端
- stdout 写入
all.txt
3. 追加模式(append)
>>:向文件末尾追加(append),不覆盖原文件2>>:同理,对 stderr 追加
1 | ./mytest >>out.log 2>>err.log |
4. Bash 的简写特性
Bash 提供了更简洁的写法,将 stdout 与 stderr 同时重定向:
&>:等价于>file 2>&1&>>:等价于>>file 2>&1
1 | ./mytest &>both.log # 将 stdout 和 stderr 一次性写入 both.log(覆盖) |
5. 形象比喻
可把 stdout(1)比作“普通信件通道”,stderr(2)比作“特别邮件通道”:
- 各走各的信道
1>normal.txt 2>error.txt- 相当于把普通信件放到 A 信箱,特别邮件放到 B 信箱。
- 合并邮件
>all.txt 2>&1- 先把普通信件都放进 C 信箱,再把特别邮件也投入到 C 信箱。
5. 总结
重定向 = 文件描述符指向改变。通过 close + dup2 等系统调用,把标准输入输出 重新绑定 到文件或设备上。
- 单独重定向
cmd 1>out.txt 2>err.txt
- 合并到同一文件
- 覆盖:
cmd >all.txt 2>&1或cmd &>all.txt - 追加:
cmd >>all.txt 2>&1或cmd &>>all.txt
- 覆盖:
- 顺序关键
2>&1始终要写在将 stdout 重定向之后,才能“接上”正确的目标。
















