理解"文件"
狭义理解
狭义的文件,就是我们日常直观认知的、存储在磁盘等持久化存储设备上的普通文件,包含文本文件、二进制文件、图片、视频、可执行程序、压缩包等。
- 磁盘是永久性存储介质,因此文件在磁盘上的存储是永久性的。数据写入之后,断电、重启系统数据不会丢失
- 磁盘上的文件本质是对文件的所有操作,都是对外设的输入和输出简称 IO
广义理解
广义的文件 是Linux内核的核心抽象:Linux下一切皆文件。在系统视角中,所有能够进行「读、写、打开、关闭」操作的硬件设备、内核资源、进程通道,全部被抽象为文件,不局限于磁盘文件。(如:键盘、显示器、硬盘、网卡...)
文件操作的归类认知
- 对于 0KB 的空文件是占用磁盘空间的
- 文件是文件属性(元数据)和文件内容的集合(文件 = 属性(元数据)+ 内容)
- 所有Linux文件操作,本质是操作文件的:文件内容 + 文件属性
系统角度
- 对文件的操作,本质就是进程对文件的操作
- 磁盘的管理者是操作系统
- 文件的读写本质不是通过 C 语言 / C++ 的库函数来操作的(这些库函数只是为用户提供方便),而是通过文件相关的系统调用接口来实现的
回顾C 语言标准文件接口
C 文件接口是 <stdio.h> 标准库提供的用户层文件操作函数 ,是编程中最常用的文件 IO 方式,底层封装了 Linux 系统调用 ,自带用户层缓冲区,操作对象是 FILE* 文件流指针
打开文件:fopen
1. 函数原型
c
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
2. 功能: 打开 / 创建指定路径的文件,建立进程与文件的关联
3. 参数
path:文件路径
相对路径(test.txt)/ 绝对路径(/home/test.txt)mode:打开模式(字符串,决定文件的读写权限、创建 / 清空 / 追加规则)
| 模式 | 含义 | 文件不存在 | 文件已存在 | 读写权限 |
|---|---|---|---|---|
r |
只读打开 | 报错 | 正常打开 | 只读 |
w |
只写打开 | 创建新文件 | 清空内容 | 只写 |
a |
追加只写 | 创建新文件 | 末尾追加 | 只写 |
r+ |
读写打开 | 报错 | 正常打开 | 读写 |
w+ |
读写打开 | 创建新文件 | 清空内容 | 读写 |
a+ |
读写追加 | 创建新文件 | 末尾追加 | 读写 |
4. 返回值
- 成功:返回
FILE*类型的文件流指针 - 失败:返回
NULL(必须做判断!)
示例:
c
#include <stdio.h>
int main() {
// 以只写方式打开文件,不存在则创建
FILE* fp = fopen("test.txt", "w");
// 必须判断打开是否失败
if (fp == NULL) {
perror("fopen error"); // 打印系统错误信息
return 1;
}
// 后续读写操作...
fclose(fp);
return 0;
}
关闭文件:fclose
1. 函数原型
c
#include <stdio.h>
int fclose(FILE *stream);
2. 核心功能
- 刷新用户层缓冲区 :把
FILE结构体中缓存的未写入数据,强制同步到文件 / 硬件 - 释放
FILE结构体占用的内存; - 底层自动调用系统调用
close,释放文件描述符; - 断开进程与文件的关联。
3. 返回值
- 成功:返回
0 - 失败:返回
EOF(-1),并设置错误码
示例:
c
#include <stdio.h>
int main() {
FILE* fp = fopen("test.txt", "w");
if(fp == NULL) {
perror("fopen failed");
return 1;
}
// 写入数据(先存入缓冲区)
fprintf(fp, "Hello World");
// 必须关闭:刷新缓冲区 + 释放资源
fclose(fp);
// 关闭后,fp 成为野指针,不能再使用!
return 0;
}
写文件
依赖头文件:#include <stdio.h>
1. fprintf:格式化写入
c
int fprintf(FILE *stream, const char *format, ...);
- 功能 :按照指定格式(和
printf完全一致)将数据写入文件流 - 参数 :
stream:目标文件流(FILE*,可以是文件 /stdout/stderr)
format:格式化字符串(%d/%s/%f等) - 特点:支持格式化输出,适合文本、日志、配置文件写入
- 代码示例
c
// 写入数字、字符串到文件
fprintf(fp, "ID: %d, Name: %s\n", 1001, "Linux");
// 写入屏幕(标准输出)
fprintf(stdout, "Hello World\n");
2. fputs:纯字符串写入
c
int fputs(const char *str, FILE *stream);
- 功能 :将纯字符串写入文件流,不自动添加换行符
- 代码示例
c
fputs("I/O Study\n", fp);
fputs("Print to Screen\n", stdout);
3. fwrite:二进制批量写入
c
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- 功能 :批量写入二进制数据(结构体、数组、图片、音频等),不做数据转换
- 参数 :
ptr:数据内存地址
size:单个数据大小
nmemb:数据个数 - 特点:效率最高,适合二进制文件、批量数据
- 代码示例
c
// 写入结构体
struct Student { int id; char name[10]; };
struct Student s = {1001, "Tom"};
fwrite(&s, sizeof(struct Student), 1, fp);
读文件
依赖头文件:#include <stdio.h>
1. fgets:按行读取
c
char *fgets(char *buf, int size, FILE *stream);
- 功能 :从文件流中一次读取一行数据 ,自动在末尾补
\0(字符串结束符) - 参数 :
buf:存放读取数据的内存缓冲区
size:缓冲区最大容量(最多读size-1字节)
stream:文件流(文件 /stdin) - 返回值 :
成功:返回buf地址
读到文件末尾 / 失败:返回NULL - 代码示例
c
char buf[1024];
// 按行读取文件
while(fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf); // 打印读取内容
}
2. fscanf:格式化读取(对应 fprintf)
c
int fscanf(FILE *stream, const char *format, ...);
- 功能 :按照指定格式(和
scanf一致)从文件中解析数据 - 适用场景:读取结构化文本(日志、配置、数字 + 字符串组合)
- 返回值 :成功返回匹配到的参数个数 ,读到末尾返回
EOF - 代码示例
c
int id;
char name[20];
// 从文件读取格式化数据:ID:1001 Name:Tom
fscanf(fp, "ID:%d Name:%s", &id, name);
3. fread:二进制批量读取(对应 fwrite)
c
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 功能 :批量读取二进制数据(结构体、图片、音频、数组),不做任何格式转换;
- 参数 :
ptr:存储数据的内存地址;
size:单个数据单元大小;
nmemb:期望读取的单元数量; - 返回值 :返回实际读取的单元个数
- 代码示例
c
struct Student { int id; char name[10]; };
struct Student s;
// 读取二进制结构体
fread(&s, sizeof(struct Student), 1, fp);
标准输入输出 stdin、stdout、stderr
- 本质 :
stdin/stdout/stderr是 C 标准 IO 提供的FILE*类型指针 ,对应 Linux 内核的 3 个默认文件描述符 - 来源:任何进程启动时,操作系统内核会自动为进程打开这 3 个广义文件 ,无需手动
open/fopen - 作用 :进程与外界(键盘 / 显示器)交互的默认 IO 通道
- 底层 :所有标准输入输出,最终都通过
read/write系统调用实现
| C 标准流指针 | 文件描述符 fd | 名称 | 默认硬件设备 | 读写权限 | 缓冲类型 | 核心作用 |
|---|---|---|---|---|---|---|
stdin |
0 | 标准输入 | 键盘 | 只读 | 行缓冲 | 进程从键盘读取数据 |
stdout |
1 | 标准输出 | 显示器 | 只写 | 行缓冲 | 进程向屏幕打印正常信息 |
stderr |
2 | 标准错误 | 显示器(和 stdout 同一个硬件,但独立通道) | 只写 | 无缓冲 | 进程向屏幕打印错误信息 |
系统文件 IO
系统文件 IO 是 Linux 操作系统提供的原生底层文件操作接口 ,也叫 Linux 系统调用IO,是所有文件操作的最终实现者
系统 IO 只有 4 个基础函数,对应打开、关闭、读、写,所有文件操作都围绕它们展开
打开文件:open函数
两个函数原型
1. 不创建文件(仅打开已有文件)
c
int open(const char *pathname, int flags);
2. 创建新文件(文件不存在则创建)
c
int open(const char *pathname, int flags, mode_t mode);
必须包含的头文件
c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> // open 专属头文件
参数详解
参数 1:pathname → 文件路径
- 作用:指定要打开 / 创建的文件路径
- 支持格式:
相对路径 :"test.txt"(当前程序所在目录)
绝对路径 :"/home/study/test.txt"(系统完整路径)
参数 2:flags → 打开标志位
flags 是位掩码 ,用 按位或 | 拼接多个功能,分为两类:
第一类:必选标志
定义文件基础读写权限,必须选且只能选 1 个:
| 宏定义 | 含义 | 说明 |
|---|---|---|
O_RDONLY |
只读打开 | 只能读,不能写 |
O_WRONLY |
只写打开 | 只能写,不能读 |
O_RDWR |
读写打开 | 可读可写 |
第二类:可选标志(任意叠加,用 | 拼接)
| 宏定义 | 含义 | 核心场景 |
|---|---|---|
O_CREAT |
文件不存在则创建 | 新建文件必备 |
O_TRUNC |
打开时清空文件原有内容 | 覆盖写入(对应 w 模式) |
O_APPEND |
追加写入(永远在文件末尾写) | 日志追加(对应 a 模式) |
参数 3:mode → 文件权限(仅创建文件时必填)
- 触发条件 :只有
flags包含O_CREAT,才需要传这个参数 - 格式要求 :必须是 8 进制数字 (以
0开头!) - 最常用权限 :
0664 - 权限规则 :实际权限 = 指定的 mode & ~系统umask(默认 umask=0002)
返回值:文件描述符 fd
- 成功 :返回 非负整数
内核默认分配:0(stdin)、1(stdout)、2(stderr)
手动打开的文件,fd从 3 开始分配(最小空闲下标) - 失败 :返回
-1,同时自动设置错误码
示例 1:创建新文件
c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt", O_CREAT | O_WRONLY, 0666);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
示例 2:覆盖写入(清空文件)
c
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
补充:传递标志位flags的方法
- Linux 里给
open传flags标志位,底层就是标准的位图传参思想 ,也叫位掩码传参 flags= 一个 32 位的整数位图- 位图 = 用一个整数的每一个二进制位,单独代表一个独立的开关状态
某一位为1→ 开启对应功能
某一位为0→ 关闭对应功能
代码示例:
c
#include <stdio.h>
#define ONE_FLAG (1<<0) // 0000 0000 0000...0000 0001
#define TWO_FLAG (1<<1) // 0000 0000 0000...0000 0010
#define THREE_FLAG (1<<2) // 0000 0000 0000...0000 0100
#define FOUR_FLAG (1<<3) // 0000 0000 0000...0000 1000
void Print(int flags)
{
if(flags & ONE_FLAG)
{
printf("One!\n");
}
if(flags & TWO_FLAG)
{
printf("Two!\n");
}
if(flags & THREE_FLAG)
{
printf("Three!\n");
}
if(flags & FOUR_FLAG)
{
printf("Four!\n");
}
}
int main()
{
Print(ONE_FLAG);
printf("\n");
Print(ONE_FLAG | TWO_FLAG);
printf("\n");
Print(ONE_FLAG | TWO_FLAG | THREE_FLAG);
printf("\n");
Print(ONE_FLAG | TWO_FLAG | THREE_FLAG | FOUR_FLAG);
printf("\n");
Print(ONE_FLAG | FOUR_FLAG);
printf("\n");
return 0;
}
关闭文件:close函数
必须包含的头文件
c
#include <unistd.h> // close 系统调用专属头文件
函数原型
c
int close(int fd);
- 参数 :
int fd→ 要关闭的文件描述符 (open返回的整数) - 返回值 :
int类型(成功 / 失败标识) - 作用:释放文件描述符,减少内核资源引用计数,断开进程与文件的绑定
写文件:write
依赖头文件
c
#include <unistd.h> // write / read / close 系统调用专属
函数原型
c
ssize_t write(int fd, const void *buf, size_t count);
- 返回值:
ssize_t(有符号整型,代表实际写入的字节数) - 参数:文件描述符、数据缓冲区、期望写入长度
参数详解
1. int fd → 文件描述符
- 来源:
open函数成功打开文件后返回的整数 - 作用:进程操作文件的唯一凭证 ,内核通过
fd找到目标文件
2. const void *buf → 数据缓冲区
- 作用:要写入的数据在进程内存中的起始地址
- 类型:
void*通用指针,支持任意类型数据(字符串、结构体、二进制数据)
3. size_t count → 期望写入的字节数
- 作用:你想写入多少字节的数据
- 限制:不能超过缓冲区的实际大小,否则会导致内存越界
返回值
write 的返回值不是固定等于 count
- > 0 :成功写入的字节数(可能 < count)
- = -1 :写入失败(权限不足、fd 无效、磁盘满等)
- = 0 :未写入任何数据(极少出现)
示例 1:覆盖写入(清空文件,从头写)
c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
const char *msg = "hello world\n";
int cnt = 5;
while(cnt)
{
// 当作字符来写
write(fd, msg, strlen(msg));
cnt--;
}
close(fd);
return 0;
}
示例 2:追加写入(不清空,末尾写)
只需要修改open的标志:
c
int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);
读文件:read函数
必须包含的头文件
c
#include <unistd.h> // read 系统调用专属
函数原型
c
ssize_t read(int fd, void *buf, size_t count);
- 返回值 :
ssize_t(有符号整型,代表实际读取到的字节数) - 参数:文件描述符、数据存储缓冲区、期望读取的最大长度
参数详解
1. int fd → 文件描述符
- 来源:
open函数成功打开文件后返回的整数 - 硬性要求 :必须以
O_RDONLY/O_RDWR模式打开
2. void *buf → 读取缓冲区
- 作用:进程内存的一块空间,用来存放从文件读取到的数据
- 类型:
void*通用指针,支持存储字符串、结构体、二进制数据
3. size_t count → 期望读取的最大字节数
- 作用:你最多想读多少字节的数据
- 限制:不能超过缓冲区的实际大小,否则会造成内存越界
返回值
- > 0 :实际读取到的字节数(成功)
- = 0 :读到文件末尾(EOF)
- = -1 :读取失败(无效 fd、权限不足、磁盘错误)
示例:
c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
while(1)
{
char buffer[64];
int n = read(fd, buffer, sizeof(buffer)-1);
if(n > 0)
{
buffer[n] = 0;
printf("%s", buffer);
}
else if(n == 0)
{
break;
}
}
close(fd);
return 0;
}
文件描述符
文件描述符 = 一个非负整数 = 进程内核空间中「文件描述符表」(struct files_struct)的数组下标
默认分配的 3 个 fd:0,1,2
任何进程启动时,内核都会自动打开 3 个标准文件,分配固定 fd:
文件描述符 fd |
名称 | 宏定义 | 默认设备 | 作用 |
|---|---|---|---|---|
0 |
标准输入 | stdin |
键盘 | 读取数据 |
1 |
标准输出 | stdout |
显示器 | 打印正常信息 |
2 |
标准错误 | stderr |
显示器 | 打印错误信息 |
示例:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
printf("stdin: %d\n", stdin->_fileno);
printf("stdout: %d\n", stdout->_fileno);
printf("stderr: %d\n", stderr->_fileno);
printf("\n\n");
umask(0);
int fd1 = open("log1.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
int fd2 = open("log2.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
int fd3 = open("log3.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
int fd4 = open("log4.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
if(fd1 < 0) exit(1);
if(fd2 < 0) exit(1);
if(fd3 < 0) exit(1);
if(fd4 < 0) exit(1);
printf("fd1: %d\n", fd1);
printf("fd2: %d\n", fd2);
printf("fd3: %d\n", fd3);
printf("fd4: %d\n", fd4);
close(fd1);
close(fd2);
close(fd3);
close(fd4);
return 0;
}
完整串联:task_struct → files_struct → fd_array → fd → struct file → 文件缓冲区

整体层级关系(从上到下)
bash
进程控制块 task_struct
↓ 内部成员:struct files_struct *files
文件描述符表 struct files_struct
↓ 内部成员:struct file *fd_array[]
文件描述符数组 fd_array[]
↓ 数组下标 = 文件描述符 fd
数组元素:struct file * 指针
↓ 指向
打开文件实例 struct file
↓ 关联
内核文件缓冲区 / inode 磁盘文件
1. 最顶层:task_struct 进程结构体
Linux 内核中,每一个进程都唯一对应一个 task_struct,它是进程的全部描述信息。里面记录进程 PID、运行状态、内存资源、信号、文件资源等所有属性。
核心关联成员:
c
struct task_struct {
// ... 其他大量成员
struct files_struct *files; // 指向该进程专属的文件描述符表
};
含义:每个进程通过 files 指针,找到自己独有的文件管理表,进程之间文件资源完全隔离
2. 第二层:struct files_struct 文件描述符表
files_struct 是专门用来统一管理当前进程所有已打开文件的结构体,一个进程仅有一份。
核心内部成员:
c
struct files_struct {
// ...
struct file *fd_array[]; // 核心:文件指针数组
// ...
};
它的作用就是托管 fd_array 数组,统筹该进程所有文件描述符
3. 第三层:fd_array [] 文件指针数组
fd_array 本质是一个存放 struct file* 类型的指针数组
- 数组下标 :就是我们代码里使用的 文件描述符 fd
- 数组元素 :存放指向内核
struct file结构体的地址
4. 第四层:struct file 打开文件结构体
进程每调用一次 open 打开文件,Linux 内核就会在内核内存中,创建一个全新独立的 struct file 结构体
核心意义
- 同一个磁盘文件,多次 open 就会创建多个 struct file
- 每一个 struct file 代表一次独立的打开状态,相互独立互不干扰
struct file 核心成员
c
struct file {
loff_t f_pos; // 文件读写偏移量(最核心)
unsigned int f_flags; // 保存open传入的flags标志位
atomic_t f_count; // 引用计数
struct inode *f_inode;// 指向磁盘真实文件inode
// 关联内核读写缓冲区
};
同时该结构体直接对接内核文件缓冲区
5. 文件缓冲区 关联关系
- 用户进程调用
read/write时,不会直接读写磁盘 - 依靠
struct file找到对应的内核页高速缓冲区 - 写流程:用户空间数据 → 内核文件缓冲区 → 异步刷入磁盘
- 读流程:磁盘数据预读到内核缓冲区 → 拷贝到用户进程缓冲区
struct file负责维护本次打开操作对应的缓冲区读写位置与权限
文件描述符的分配规则
文件描述符的分配原则:最小未被使用的下标优先分配
示例:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
//close(2);
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
printf("fd: %d\n", fd);
return 0;
}
发现是结果是: fd: 0 或者 fd: 2 ,可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符
如果我们将代码改为 close(1),我们会发现本来输出到显示器上的内容,输出到了文件log.txt当中。其中,fd=1。这种现象叫做输出重定向。
重定向的本质
重定向的本质 = 偷换标准文件描述符的指向
| 重定向类型 | 操作 fd | 本质(指针替换) |
|---|---|---|
输出重定向 > |
1 | fd=1:显示器 → 文件 |
输入重定向 < |
0 | fd=0:键盘 → 文件 |
追加重定向的本质 = 标准输出重定向 + 追加写入模式(O_APPEND),fd 指向修改逻辑完全相同,仅文件写入规则不同

dup2 系统调用
dup2 ------ 它是 Linux 实现重定向最标准、最底层的系统调用 ,也是命令行 >/>> 重定向的真正底层实现 (shell 内部就是用 dup2 做重定向,而非手动 close+open)
核心作用:强制让 newfd 成为 oldfd 的副本,两个文件描述符指向同一个内核 struct file 结构体;如果 newfd 已被占用,内核会自动先关闭它 ,全程一步完成
函数原型 & 头文件
c
#include <unistd.h>
// 函数原型
int dup2(int oldfd, int newfd);
参数解释
oldfd:源文件描述符 (必须是有效、已打开的 fd,比如我们打开文件得到的 3)newfd:目标文件描述符 (我们强制指定的 fd,比如标准输出 1、标准输入 0)
返回值
- 成功 :返回
newfd(和你指定的目标 fd 一致) - 失败 :返回
-1(比如 oldfd 无效)
示例1:用 dup2 实现标准输出重定向
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);
//int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);
if(fd < 0) exit(1);
dup2(fd, 1);
// 默认向显示器输出
printf("fd: %d\n", fd);
printf("hello world\n");
fprintf(stdout, "hello stdout\n");
close(fd);
return 0;
}
示例2:用 dup2 实现标准输入重定向
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt", O_RDONLY);
if(fd < 0) exit(1);
dup2(fd, 0);
close(fd);
while(1)
{
char buffer[64];
if(!fgets(buffer,sizeof(buffer), stdin)) break;
printf("%s", buffer);
}
return 0;
}
在minishell中添加重定向功能
总流程
- 解析命令行:分离「命令参数」和「重定向信息(类型 + 文件名)」
- fork 子进程
- 子进程核心 :打开文件 →
dup2重定向 → 关闭多余 fd →exec执行命令 - 父进程:等待子进程结束,不做任何重定向操作
完整代码:
略
补充:标准错误
前置回顾
- 进程启动,内核自动打开 3 个独立文件描述符:
fd=0(输入)、fd=1(标准输出)、fd=2(标准错误) - 重定向
>的完整写法是1>,只修改 fd=1 的指向
标准错误的本质
标准错误 = 文件描述符 fd=2
- 它是进程
fd_array[]数组的下标 2 - 对应一个独立的
struct file内核结构体(和 fd=1 完全分开) - 默认指向显示器(和 fd=1 终点相同,但管道独立)
只重定向标准错误用法
bash
./a.out 2> log.error
先看一个现象:

我们可以发现:只重定向了 fd=1 :将 fd_array[1] 从显示器 → log.txt,fd=2 没有任何修改:依旧指向显示器
那如果我们要stderr和stdout打印到同一个文件呢?
bash
./a.out 1> log.txt 2>&1
1> log.txt:完整的标准输出重定向2>&1:把fd=2重定向到fd=1当前指向的位置
为什么要存在标准错误?
1. 重定向隔离
- 正常输出(fd=1)可以重定向到文件
- 错误信息(fd=2)必须留在屏幕,让你立刻看到报错
2. 日志分离
可以把正常日志 和错误日志 分开存:
示例:
