当前位置: 首页 > news >正文

标准输入流,输出流,错误流 以及 重定向 的原理

标准输入流、输出流、错误流在操作系统与C语言中的表达

1. 操作系统层面(Linux/Unix)

在操作系统层面,标准输入、标准输出和标准错误流通过文件描述符(File Descriptor)来标识:

流类型文件描述符 (fd)默认设备
标准输入0通常是终端/键盘
标准输出1通常是终端/屏幕
标准错误2通常是终端/屏幕

这些文件描述符本质上是进程打开的文件句柄,可以指向终端设备、普通文件或管道。

2. C语言层面

在C语言中,标准库<stdio.h>提供了三个预定义的FILE*指针:

#include<stdio.h>// 三种标准流指针stdin// 标准输入流stdout// 标准输出流stderr// 标准错误流

对应关系

C语言流文件描述符说明
stdin0fgets()、scanf() 从此读取
stdout1printf()、puts() 往此写入(有缓冲)
stderr2fprintf(stderr, …) 往此写入(无缓冲)

示例代码

#include<stdio.h>#include<unistd.h>// for close()intmain(){// C语言方式使用标准流fprintf(stdout,"正常输出\n");fprintf(stderr,"错误信息\n");// 获取底层文件描述符intfd_in=fileno(stdin);// 0intfd_out=fileno(stdout);// 1intfd_err=fileno(stderr);// 2// 直接使用系统调用写入write(fd_out,"hello\n",6);return0;}

3. stdout 与 stderr 的关键区别

特性stdoutstderr
缓冲有缓冲(行缓冲或全缓冲)无缓冲(立即写出)
用途程序正常输出错误/诊断信息
重定向可单独重定向可单独重定向

重定向示例

# 只重定向 stdout 到文件./prog>output.txt# 只重定向 stderr 到文件./prog2>error.log# 将 stdout 和 stderr 都重定向到同一文件./prog>all.txt2>&1

4. 重定向的实现原理

./prog > output.txt这个重定向是如何实现的?

这是通过shell 在调用程序前修改文件描述符实现的。核心机制如下:

  1. >是 shell 的语法,不是程序本身的行为
  2. shell 会:
    • 打开目标文件
    • 把文件的文件描述符设为 1(标准输出)
    • 再执行你的程序
  3. 程序看到的 fd 1 已经不是终端,而是文件了

用系统调用还原./prog > output.txt

// shell 实际做的事(简化版)pid_tpid=fork();if(pid==0){// 子进程:执行重定向intfd=open("output.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd,1);// 让 fd 1 指向文件close(fd);// 执行目标程序execvp("./prog",argv);}

dup2(fd, 1)的作用是:让文件描述符 1 和 fd 指向同一个文件。之后程序里所有的printf()write(1, ...)都写到文件里了。

验证实验

// test.c#include<stdio.h>#include<unistd.h>intmain(){printf("hello\n");// 走 stdout (fd 1)write(1,"world\n",6);// 直接写 fd 1return0;}
$ gcc test.c-otest$ ./test# 输出到屏幕$ ./test>out.txt# 输出到文件$catout.txt# 输出:hello\nworld\n

程序代码完全没变,变的只是 shell 在 exec 前把 fd 1 换成了文件。

5. 重定向的恢复问题

dup2(fd, 1)后,何时重新把 1 指回标准输出流?

不会自动指回来——重定向的影响会持续到这个进程结束。

dup2(fd, 1)之后,这个进程的 fd 1 就永远指向文件了,直到:

  1. 进程退出(所有文件描述符自动关闭)
  2. 进程自己再用dup2()改回去

shell 为什么能"恢复"?

因为 shell 是在子进程中做重定向:

+------------------+ | 父 shell 进程 | | fd 1 = 终端 | ← 从来不会被改变 +------------------+ | fork() | v +------------------+ | 子进程 | | fd 1 = 终端 | ← 刚 fork 时继承自父进程 | dup2(fd, 1) | | fd 1 = 文件 | ← 只有子进程被改了 | exec("./prog") | +------------------+ | 退出 v 子进程销毁,fd 全部关闭

父 shell 的 fd 1 一直是终端,不受影响。

在程序内部恢复的方法

如果想在程序内部恢复,可以在重定向前先保存原来的 fd 1:

#include<stdio.h>#include<unistd.h>#include<fcntl.h>intmain(){// 保存原始标准输出intsaved_stdout=dup(1);// 复制 fd 1,得到比如 fd 3// 重定向到文件intfd=open("output.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd,1);close(fd);// ... 现在 printf 都写到文件 ...printf("写入文件\n");// 恢复标准输出dup2(saved_stdout,1);// 把 fd 1 改回原来的终端close(saved_stdout);// ... 现在 printf 又写到屏幕了 ...printf("回到屏幕\n");return0;}

6. 总结

场景何时恢复
shell 重定向 (./prog > out.txt)子进程退出,父进程从未受影响
程序内自己dup2重定向不会自动恢复,需要手动dup2(saved_fd, 1)

通过理解文件描述符、标准流和重定向机制,可以更好地掌握Linux/Unix系统编程和C语言中I/O操作的本质。

http://www.zskr.cn/news/1324847.html

相关文章:

  • 手把手教你用MATLAB搞定车载固态LiDAR与RTK的自动标定(附避坑指南)
  • 嵌入式Linux设备搭建无线AP:从hostapd配置到NAT优化的完整指南
  • Minecraft 1.21必备:5分钟搞定Masa模组全家桶中文汉化终极指南
  • N_m3u8DL-RE:跨平台流媒体下载器的终极解决方案
  • Python浮点精度陷阱——0.1+0.2≠0.3的底层原因与解决方案
  • TypeScript-------------类型收窄
  • 2026年5月均三嗪供应商深度解析与实力派推荐 - 2026年企业推荐榜
  • OK3568开发板配置流程
  • 小白/程序员必看:轻松掌握MCP协议,让大模型调用工具不再难(收藏版)
  • Ubuntu 16.04 32位系统下RT-Thread开发环境搭建全攻略
  • 川南二手物资回收服务机构2026年客观排行一览:宜宾荣生其商贸有限公司联系/办公座椅回收/办公设备回收/大型卖场回收/选择指南 - 优质品牌商家
  • 别再为ST7789屏幕移植发愁了!一份代码搞定STM32/51/Arduino(附完整工程)
  • Purple Pi OH开发板适配OpenHarmony 5.0全流程解析与实战
  • 最近被黑产盯上了,用我的帐号发了duboo信息,这不是我发的
  • 检索增强生成RAG基础架构与手动模拟
  • MTK工具箱进阶玩法:备份手机NV基带、解包Super.img,再也不怕信号丢失
  • 如何快速部署AI视觉瞄准系统:3个版本满足不同需求的终极指南
  • 2026 AI低代码实测:原理拆解+主流形态,避坑指南
  • 3步掌握TEdit地图编辑器:泰拉瑞亚终极创作工具完全指南
  • 3步彻底解决Windows程序启动失败:VisualCppRedist AIO终极修复指南
  • B站缓存视频转换完全指南:让珍贵内容真正属于你
  • 实时商业情报不再滞后,Perplexity新闻搜索配置全拆解,从入门到日均处理200+信源
  • 避开移相内卷:手把手推导DAB变频控制的传递函数,搞定PI参数设计
  • Perplexity症状查询功能突然失效?排查清单来了:从OpenID Connect令牌过期、UMLS MetaMap服务中断到本地缓存污染的6层故障树分析
  • Perplexity股票数据清洗SOP(含NASDAQ非标字段映射表):金融工程师内部使用的12项校验规则
  • 别再傻傻分不清了!图像分割模型评估:Dice系数 vs. IOU,到底该用哪个?
  • ddraw.dll 怎么修复?按电脑小白能看懂的步骤来
  • 苹果Siri 2025全面升级:从LLM集成到系统级智能体的技术路径解析
  • 芯片封装技术全解析:从Wire Bonding到先进封装的选型与实战
  • 创维E900V21D刷机后必做的5个优化:从卡顿盒子到流畅电视系统的完整设置