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.txt
2>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 重定向之后,才能“接上”正确的目标。