Linux:文件描述符与重定向
目录
一、文件描述符
1.文件内核对象
2.文件描述符分配原则
二、文件重定向
1.重定向的现象
输出重定向
输入重定向
dup2
2.重定向的使用
三、标准输出和标准错误
继上篇文章中,我们了解了fd打印的值为文件描述符,那么它还有什么作用呢?
一、文件描述符
1.文件内核对象
我们学习了open函数,知道了文件描述符就是一个整数。Linux 进程默认情况下会有 3 个缺省打开的文件描述符,分别是标准输入 0, 标准输出 1, 标准错误 2。0、1、2 对应的物理设备一般是:键盘、显示器、显示器。
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
输入输出程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{char buf[1024];// 定义一个1024字节的缓冲区ssize_t s = read(0, buf, sizeof(buf));//从标准输入(stdin)读取数据到缓冲区bufif(s > 0)// 检查是否成功读取到数据{buf[s] = 0; // 手动添加字符串终止符'\0'write(1, buf, strlen(buf));// 将数据写入标准输出write(2, buf, strlen(buf));// 将数据写入标准错误//write() 是直接写入内核,无需手动刷新缓冲区。}return 0;
}
运行后,输出一个yes,就会再往显示屏打印两个yes
write是如何通过fd找到要打印的文件的呢?
重新理解文件描述符表:


站在用户的角度,对于文件的操作有这些诸如read、write这些系统调用,也有这些系统调用进行了一定的封装后诞生的C语言库函数,这些系统调用的内部会在内存中进行一系列操作,当程序运行的时候,进程里有个叫PCB的结构体,进程的PCB中有这么一块专门的区域名字叫作fd_array[],它的本质是一个指针数组,它会指向一个一个结构体,而每一个结构体中存储的是关于这个文件的信息,这个结构体叫做struct file,而前面所说的文件描述符fd,本质上就是这个指针数组的下标。系统默认会打开三个文件:标准输入、输出和错误。比如你调用read或write的时候,系统会根据fd找到对应的file结构体,然后操作对应文件。不过数据读写不是直接和硬盘打交道的——因为CPU只能和内存玩,所以读写前得把数据先从硬盘加载到内存里的文件缓冲区里,进程才能从中读写。不管读还是写,都是在内核缓冲区和用户空间之间倒腾数据,这也就是为什么系统调用看起来像是在内存里搞事情的原因啦。 结论:在应用层对于数据的读写,本质上是将内核缓冲区的数据进行来回拷贝。
2.文件描述符分配原则
我们知道因为系统会默认打开三个文件,分别占用0,1,2这三个位置, 那么把前面这几个关掉呢?
关闭0:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>int main()
{close(0);int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}
运行结果(关闭2也是一样的效果):
由此可以推测出,文件描述符的分配规则是:寻找最小的,没有被使用的数据的位置,分配给指定的打开文件
二、文件重定向
1.重定向的现象
刚刚其实我们已经细微的发现了重定向了,但是现在我们将要学的更加细:
输出重定向
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>// 重定向
void test()
{close(1);int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);if(fd < 0){perror("open fail\n");exit(1);}printf("fd-> %d\n", fd);printf("stdout->fd: %d\n", stdout->_fileno);fflush(stdout);close(fd);
}int main()
{test();return 0;
}
运行结果:
运行结果是,在屏幕上没有任何信息,但在log.txt中居然出现了输出信息。
当程序通过关闭标准输出(fd=1)并重新打开一个文件(如log.txt)时,由于文件描述符遵循"用完即丢"的复用规则,新文件会抢占原本属于标准输出的1号索引位置。此时所有以1为默认目标的输出操作(包括printf)都会被悄悄重定向——它们不再往显示器写数据,而是像对待普通文件一样向log.txt写入内容。这个过程中,Linux内核通过VFS将设备抽象成统一的文件对象,使得无论stdout原本指向的是屏幕还是磁盘文件,最终都表现为对特定文件描述符的操作。这种设计巧妙利用了文件描述符的索引特性,实现了I/O重定向的核心逻辑:系统只认数字标号,不关心具体绑定的物理设备,真正实现了"一切皆文件"的统一管理理念。总结:新打开的文件描述符取代了stdout原先的描述符,大家只认fd==1的文件。

输入重定向
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>int main()
{close(0);int fd = open("log.txt", O_RDONLY);if (fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);char buf[64];fgets(buf, sizeof buf, stdin);printf("%s", buf);return 0;
}

重定向的本质,就是修改特征文件fd的下标内容。上层的fd不变,变化的是底层fd指向的内容,也就是所谓文件描述符级别的数组内容的拷贝。那这样的写法还是太奇怪了,每次都要把一个文件关闭再打开一个新的文件,作为系统理应给操作者提供这样替换文件描述符的系统调用,事实上也确实提供了这样的系统调用。
dup2
SYNOPSIS#include <unistd.h>int dup(int oldfd);int dup2(int oldfd, int newfd);
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
简单来说,就是用oldfd去替换newfd,保留下来的是oldfd,那么上面的代码就可以被改良成这样:
void redir2()
{int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);if(fd < 0){perror("open fail\n");exit(1);}dup2(fd, 1);printf("fd-> %d\n", fd);printf("stdout->fd: %d\n", stdout->_fileno);fflush(stdout);close(fd);
}
运行结果:

fd->3,fd也没有变化,但是就是没有往显示屏上打印,依旧是往文件里打印的。也就是说,不需要你去关闭一个文件秒速符,而是用一个文件描述符暂时的代替另外一个文件描述符,这样不仅更加准确,而且更加明了。

要输出的文件描述符是 1,而要重定向的目标文件描述符是 fd (echo “hello” > log.txt),dup2 应该怎么传参 —— dup2(1, fd) || dup2(fd, 1) ?
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
很明显依靠函数原型,我们就能认为 dup2(1, fd),因为 1 是先打开的,而 fd 是后打开的.可实际上并不是这样的,文档中说 newfd 是 oldfd 的一份拷贝,这里拷贝的是文件描述符对应数组下标的内容,所以数组内容最终应该和 oldfd 一致。换而言之,这里就是想把让 1 不要指向显示器了,而指向 log.txt,fd 也指向 log.txt。所以这里的 oldfd 对应 fd,newfd 对应 1,所以应该是 dup2(fd, 1)。
oldfd copy to newfd -> 最后要和谁一样? oldfd
假设输出重定向:显示器(1) -> log.txt(3)。应该是 dup2(1, 3);,还是dup2(3, 1); ?
dup2(3, 1);
3 的内容 copy 到 1 里面 -> 最终和 3 一致
2.重定向的使用
重定向其实并不陌生,在之前的学习中已经用过重定向,只是那是还没有建立起来一个基础的概念,先看下面的指令演示:
[root@iZbp1157ft1ib0ydj8jqtzZ test]# echo "hello linux" > log.txt
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat log.txt
hello linux
这段指令的含义就是,把hello linux重定向到log.txt中,其实这样的操作符还有下面的这几种,一一进行介绍
>:这个操作符表示的是输出重定向,意思就是把内容输出到某个文件中,有些类似于以w的方式打开一个文件并进行写入。
>>:这个操作符表示的是追加重定向,意思就是把内容追加输出到某个文件中,有些类似于append的方式进行写入。
<:这个操作符表示的是输入重定向,表示把原来的内容输入输入到某个文件中,相当于是替换了标准输入流的文件。
三、标准输出和标准错误
前面的知识已经足以理解为什么要有标准输入和标准输出,但是还有一个问题有待解决,标准错误的意义是什么呢?难道标准输出的信息还不够吗?
答案是确实不够,在对于大型项目的时候,会有很多的输出信息,这些输出信息有些是正常信息,有些是异常信息,而对于开发者来说他们需要的是错误信息,因此对于如何获取错误信息就显得至关重要,于是标准错误流信息就诞生了,对于正常来说可能没有太多的感觉,但是实际上,没有感觉的原因是因为标准错误和标准输出的文件对象都是显示器,而实际上这是可以被替换的,基于这个原理可以做出下面的测试代码:
int main()
{printf("this is normal message\n");perror("this is error message\n");return 0;
}
运行和操作结果:
[root@iZbp1157ft1ib0ydj8jqtzZ test]# ./test 1>out.txt 2>error.txt
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat out.txt
this is normal message
[root@iZbp1157ft1ib0ydj8jqtzZ test]# cat error.txt
this is error message
: Success
利用上面的原理可以写出这样的测试代码,把正确信息存储到一个文件中,把错误信息存储到另外一个文件中,这样就能知道哪里是错误哪里是正确了。
1>&2意思是把标准输出重定向到标准错误。
2>&1意思是把标准错误输出重定向到标准输出。
相关文章:
Linux:文件描述符与重定向
目录 一、文件描述符 1.文件内核对象 2.文件描述符分配原则 二、文件重定向 1.重定向的现象 输出重定向 输入重定向 dup2 2.重定向的使用 三、标准输出和标准错误 继上篇文章中,我们了解了fd打印的值为文件描述符,那么它还有什么作用呢&…...
【原创】C# HttpClient 读取流数据的问题
默认情况下HttpClient中有缓存,在读取流数据的时候,往往要等一小会儿,然后读出一大堆。 我们在请求OpenAI类的大模型的时候,往往要一边读取一边显示(输出),这时候需要禁止HttpClient 中内置的缓…...
C# 开发工具Visual Studio下载和安装
开发环境与工具 C#的主要开发环境是Visual Studio,这是一个功能强大的集成开发环境(IDE),集成了代码编辑、调试、项目管理、版本控制等功能。此外,Visual Studio Code也是一个轻量级的跨平台代码编辑器,支…...
3-7 WPS JS宏 工作表移动复制实例-2(多工作簿的多工作表合并)学习笔记
************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…...
Python在机器学习与数据分析领域的深度应用:从基础到实战
在当今数字化时代,数据如同宝贵的矿产资源,蕴含着无尽的价值等待挖掘。Python作为一门强大而灵活的编程语言,凭借其丰富的库和工具,在机器学习和数据分析领域扮演着举足轻重的角色。它不仅为数据科学家和开发者提供了高效处理和分…...
网络安全ctf试题 ctf网络安全大赛真题
MISC 1 签到 难度 签到 复制给出的flag输入即可 2 range_download 难度 中等 flag{6095B134-5437-4B21-BE52-EDC46A276297} 0x01 分析dns流量,发现dns && ip.addr1.1.1.1存在dns隧道数据,整理后得到base64: cGFzc3dvcmQ6IG5zc195eWRzIQ 解…...
分布式和微服务的理解
分布式系统和微服务是现代化软件架构中两个关键概念,它们共同支撑了高可用、高扩展的互联网应用,但侧重点和解决的问题有所不同。以下是它们的核心理解: 一、分布式系统(Distributed System) 定义: 分…...
Embedding技术:DeepWalkNode2vec
引言 在推荐系统中,Graph Embedding技术已经成为一种强大的工具,用于捕捉用户和物品之间的复杂关系。本文将介绍Graph Embedding的基本概念、原理及其在推荐系统中的应用。 什么是Graph Embedding? Graph Embedding是一种将图中的节点映射…...
基于IMM算法的目标跟踪,四模型IMM|三维环境|4个模型分别是:CV、左转CT、右转CT、CA(基于EKF,订阅专栏后可获得完整源代码)
这段MATLAB代码实现了基于交互多模型(IMM)算法的目标跟踪,结合了四种运动模型(匀速直线、左转圆周、右转圆周和匀加速直线)。通过定义状态方程、生成带噪声的测量数据,以及执行IMM迭代,该代码有效地实现了多模型的状态估计和融合。最终,用户可以通过可视化结果观察目标…...
大模型工程师日记(十三):检索增强生成(RAG)
Document loaders和Text splitters Document loaders(文档加载器) Document loaders(文档加载器) 这些类加载文档对象。LangChain与各种数据源有数百个集成,可以从中加载数据:Slack、Notion、Google Drive等。 每个文档加载器都有自己特定的参数&#…...
HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect
目录 useEffect vs useLayoutEffectuseEffectuseLayoutEffect主要区别总结选择建议注意事项 useLayoutEffect 使用示例测量 DOM 元素的尺寸和位置示例:自适应弹出框定位 同步更新样式以避免闪烁示例:根据内容动态调整容器高度 图像或 Canvas 绘制前的准备…...
前端开发10大框架深度解析
摘要 在现代前端开发中,框架的选择对项目的成功至关重要。本文旨在为开发者提供一份全面的前端框架指南,涵盖 React、Vue.js、Angular、Svelte、Ember.js、Preact、Backbone.js、Next.js、Nuxt.js 和 Gatsby。我们将从 简介、优缺点、适用场景 以及 实际…...
图像形成与计算机视觉基础
1. 图像形成的基本原理 图像形成是物理世界与传感器(如胶片、CCD/CMOS)交互的过程,核心是光线的传播与记录。 1.1 直接放置胶片模型 物理原理:物体表面反射的光线直接照射到胶片上,但无任何遮挡或聚焦机制。 问题&a…...
【显示】3.1 Android 从Activity到Display链路概括
目录 一,Activity上屏Flow总结 二,链路拆解 2.1 Activity 的创建和 UI 初始化 2.2 Window 和 DecorView 的创建 2.3 Surface 的创建 2.4 View 的绘制流程 2.5 Surface 的提交和合成 2.6 上屏显示 三,多个Activity的处理方式 一,Activity上屏Flow总结 Activity → s…...
【leetcode hot 100 240】搜索二维矩阵Ⅱ
解法一:直接查找 class Solution {public boolean searchMatrix(int[][] matrix, int target) {for(int i0; i<matrix.length; i){for(int j0; j<matrix[0].length; j){if(matrix[i][j]>target){break;}if(matrix[i][j]target){return true;}}}return fal…...
Spring Boot 缓存最佳实践:从基础到生产的完整指南
Spring Boot 缓存最佳实践:从基础到生产的完整指南 引言 在现代分布式系统中,缓存是提升系统性能的银弹。Spring Boot 通过 spring-boot-starter-cache 模块提供了开箱即用的缓存抽象,但如何根据业务需求实现灵活、可靠的缓存方案…...
Ubuntu20.04双系统安装及软件安装(一):系统安装
Ubuntu20.04双系统安装及软件安装(一):系统安装 Ubuntu系统卸载Ubuntu20.04安装BIOS进入系统安装 许久没写博客了,今天开始重新回归了。首先记录我在双系统上重装Ubuntu20.04的安装过程记录以及个人见解。 Ubuntu系统卸载 参考双…...
Linux14-io多路复用
UDP:单循环服务器,服务器同一时刻只能响应一个客户端的请求 TCP:并发服务器,服务器同一时刻只能响应多个客户端的请求 一、构建TCP并发服务器 让TCP服务端具备同时响应多个客户端的能力。 1.多进程 资源消耗大,同资源平台下,并发量小。 2.多线程 创建线程、进程,比…...
【人工智能学习之优化为什么会失败】
【人工智能学习之优化为什么会失败与方案建议】 一、优化为什么会失败?1. 局部极小值和鞍点2. 梯度消失/爆炸(Vanishing/Exploding Gradients)2. 病态条件(Ill-Conditioning)3. 参数初始化不当4. 学习率不当5. 过拟合&…...
flask学习2-应用(博客)
flask学习2-应用(博客) 项目目录应用程序工厂连接到数据库建表初始化数据库文件蓝图和视图第一个视图:注册注册登录根据用户id查询用户注销模板基本布局注册登录注册用户静态文件博客蓝图索引创建更新-根据id查询更新-根据id更新删除使项目可安装描述项目安装项目测试覆盖率…...
Next.js项目实战-ai助手帮我写文章发布视频第1节(共89节)
😂Ai在国内外已经杀疯了,老板要求我们把速度再提升快一些,哪怕是几秒,几百毫秒也行~现在,马上就要,就地就要,只好搬出前端服务端(大保健)😓。没错,今天我要分…...
探秘Transformer系列之(9)--- 位置编码分类
探秘Transformer系列之(9)— 位置编码分类 文章目录 探秘Transformer系列之(9)--- 位置编码分类0x00 概述0x01 区别1.1 从直观角度来看1.2 从模型处理角度来看1.3 优劣 0x02 绝对位置编码2.1 基础方案2.2 训练式2.3 三角函数式2.4…...
文件操作(详细讲解)(2/2)
你好呀这里是我说风俗,各位客官走过路过,关关注,点点赞,收收藏,您的鼓励是对我最大的认可,我也会努力更行下去的!!!大一学生不易(》《) 5. 文件的…...
笔记四:C语言中的文件和文件操作
Faye:只要有正确的伴奏,什么都能变成好旋律。 ---------《寻找天堂》 目录 一、文件介绍 1.1程序文件 1.2 数据文件 1.3 文件名 二、文件的打开和关闭 2.1 文件指针 2.2.文件的打开和关闭 2.3 文件读取结束的判定 三、 文件的顺序读写 3.1 顺序读写…...
Zabbix+Deepseek实现AI告警分析(非本地部署大模型版)
目录 前言技术架构DeepSeek API获取1. 注册账号2. 申请API-Key Zabbix告警AI分析 实现1. 创建Scripts2. Scripts关键参数说明3. 需要注意 测试参考链接 前言 最近手伤了,更新频率下降…… 近期在Zabbix社区看到了一篇文章:张世宏老师分享的《Zabbix告警分…...
基于Celery+Supervisord的异步任务管理方案
一、架构设计背景 1.1 需求场景分析 在Web应用中,当遇到以下场景时需要异步任务处理方案: 高延迟操作(大文件解析/邮件发送/复杂计算)请求响应解耦(客户端快速响应)任务队列管理(任务优先级/…...
国产NAS系统飞牛云fnOS深度体验:从运维面板到博客生态全打通
文章目录 前言1. 飞牛云本地部署1Panel2. 1Panel功能介绍3. 公网访问1Panel控制面板4. 固定1Panel公网地址5. 1Panel搭建Halo博客6. 公网访问Halo个人博客 前言 嘿,小伙伴们!是不是厌倦了服务器管理的繁琐和搭建个人网站的复杂?今天就来一场…...
使用QT + 文件IO + 鼠标拖拽事件 + 线程 ,实现大文件的传输
第一题、使用qss,通过线程,使进度条自己动起来 mythread.h #ifndef MYTHREAD_H #define MYTHREAD_H#include <QObject> #include <QThread> #include <QDebug>class mythread : public QThread {Q_OBJECT public:mythread(QObject* …...
【LeetCode 热题 100】438. 找到字符串中所有字母异位词 | python 【中等】
继续学!嗨起来!!!(正确率已经下30%了,我在干什么) 题目: 438. 找到字符串中所有字母异位词 给定两个字符串 s 和 p,找到 s 中所有 p 的子串,返回这些子串的…...
博查搜索API日调用量突破3000万次,达到Bing API的1/3。
根据第三方机构统计,2024年Bing Search API 全球日均调用量为1.1亿次。截至2025年3月,博查 Search API日均调用量已达到3000万次(约为Bing的1/3),承接着国内AI应用60%的联网搜索请求。...
