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

C语言标准流实战:stdin/stdout/stderr重定向的5个常见场景与代码示例

C语言标准流实战:stdin/stdout/stderr重定向的5个常见场景与代码示例

在Linux系统编程中,标准输入输出流的重定向就像给程序安装了一个智能导流系统。想象一下,你正在调试一个服务器程序,突然发现关键错误信息被淹没在海量日志中——这就是stderr分离的价值。本文将用5个真实场景,带你掌握C语言标准流操控的核心技巧。

1. 日志分级存储:分离stdout与stderr

后台服务程序最头疼的问题就是日志混杂。下面这段代码演示如何将常规输出和错误日志分别写入不同文件:

#include <stdio.h> #include <unistd.h> int main() { // 模拟正常业务流程 for(int i=0; i<5; i++) { printf("Processing item %d\n", i); // stdout if(i == 3) { fprintf(stderr, "[ERROR] Invalid data at index %d\n", i); // stderr } sleep(1); } return 0; }

编译运行时的重定向技巧:

$ ./logger > output.log 2> error.log

关键点对比:

流类型默认输出缓冲方式典型重定向符号
stdout屏幕行缓冲>
stderr屏幕无缓冲2>

提示:在紧急调试时,可以用2>&1将stderr合并到stdout,便于实时查看所有输出

2. 自动化测试:用文件模拟用户输入

单元测试中经常需要模拟各种输入场景。这个例子展示如何用文本文件驱动测试流程:

#include <stdio.h> #include <string.h> #define MAX_LEN 100 void process_command(const char* input) { if(strcmp(input, "start") == 0) { printf("System initialized\n"); } else if(strcmp(input, "shutdown") == 0) { fprintf(stderr, "WARNING: System will power off\n"); } else { printf("Unknown command: %s\n", input); } } int main() { char buffer[MAX_LEN]; while(fgets(buffer, MAX_LEN, stdin)) { buffer[strcspn(buffer, "\n")] = '\0'; // 去除换行符 process_command(buffer); } return 0; }

测试用例文件test_input.txt内容:

start invalid shutdown

执行测试:

$ ./test_program < test_input.txt > test_output.log 2> test_error.log

3. 管道处理:多程序协作的数据流水线

Linux管道符|的本质就是stdout到stdin的重定向。这个数据清洗示例展示了三个程序的协作:

/* data_generator.c - 生成测试数据 */ #include <stdio.h> int main() { for(int i=0; i<10; i++) { printf("%d,%d,%d\n", i, i*i, i%3); } return 0; } /* data_filter.c - 过滤异常数据 */ #include <stdio.h> int main() { int a,b,c; while(scanf("%d,%d,%d", &a, &b, &c) == 3) { if(c != 0) { printf("%d,%d\n", a, b); } else { fprintf(stderr, "Filtered: %d\n", a); } } return 0; } /* data_analyzer.c - 数据分析 */ #include <stdio.h> int main() { int x,y, sum=0; while(scanf("%d,%d", &x, &y) == 2) { sum += y; printf("Current sum: %d\n", sum); } return 0; }

构建数据处理流水线:

$ ./data_generator | ./data_filter 2> filtered.log | ./data_analyzer

4. 动态重定向:运行时切换输出目标

某些场景需要在程序运行过程中灵活切换输出目标。这个配置解析器示例展示了freopen的用法:

#include <stdio.h> #include <stdlib.h> void set_log_file(const char* filename) { if(freopen(filename, "a", stdout) == NULL) { perror("Failed to redirect stdout"); exit(EXIT_FAILURE); } setvbuf(stdout, NULL, _IOLBF, 0); // 设置为行缓冲 } int main(int argc, char** argv) { printf("Initializing system...\n"); if(argc > 1) { set_log_file(argv[1]); } printf("Configuration loaded\n"); fprintf(stderr, "Debug messages still go to stderr\n"); return 0; }

运行示例:

$ ./redirect_demo # 输出到屏幕 $ ./redirect_demo app.log # 输出重定向到文件

5. 错误处理进阶:结合errno与stderr

系统调用错误需要精确诊断。这个文件操作示例展示了错误处理的完整流程:

#include <stdio.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <unistd.h> void try_open_file(const char* filename) { int fd = open(filename, O_RDONLY); if(fd == -1) { int err = errno; fprintf(stderr, "[%s] Failed to open file: %s (errno=%d)\n", __TIME__, strerror(err), err); return; } char buffer[256]; ssize_t n = read(fd, buffer, sizeof(buffer)-1); if(n == -1) { perror("Read error"); } else { buffer[n] = '\0'; printf("File content: %s\n", buffer); } close(fd); } int main() { try_open_file("existing.txt"); try_open_file("nonexistent.txt"); return 0; }

典型输出:

File content: Hello world [14:25:36] Failed to open file: No such file or directory (errno=2)

错误处理要点:

  1. 立即保存errno值,避免被后续调用覆盖
  2. 使用perror或strerror转换错误代码
  3. 在错误消息中包含时间戳等上下文信息
  4. 通过stderr确保错误信息及时显示

掌握这些标准流操控技巧后,你可以:

  • 构建更健壮的日志系统
  • 设计灵活的自动化测试方案
  • 创建高效的数据处理管道
  • 实现动态配置的输出系统
  • 编写专业的错误处理逻辑
http://www.jsqmd.com/news/579035/

相关文章:

  • 边缘检测算法选型指南:从Sobel到Canny的5个实际场景对比(含医疗/自动驾驶案例)
  • Vivado Linux版安装空间不足?手把手教你如何优化磁盘空间分配
  • OpenClaw监控方案:Qwen3.5-9B任务执行日志与告警配置
  • OpenClaw外设控制探索:百川2-13B-4bits量化版驱动智能家居联动
  • OpenClaw安全实践:Qwen2.5-VL-7B本地化处理敏感图片数据
  • OpenClaw技能扩展实战:Qwen3-14b_int4_awq驱动Markdown文章自动发布
  • OpenClaw跨平台控制:Qwen3.5-9B管理多台电脑
  • ROI 崇拜可能对商业有害
  • 从UI稿到上线:我是如何把‘滑动标尺’这个需求在微信小程序里落地的(踩坑复盘)
  • PVN3D ORT Custom Ops 实现记录
  • hashid使用教程
  • Windows 11上保姆级教程:用Ollama本地部署DeepSeek-R1 8B,再也不用担心API费用和网络延迟了
  • 保姆级教程:从原始counts矩阵到Seurat对象,手把手完成单细胞RNA-seq质控与过滤(附R代码避坑点)
  • STM32标准库开发入门与实战指南
  • 从ASCII码到Word模板:深入理解Apache POI中(char)11这个‘竖直制表符’的妙用
  • 央企技术岗职场生态与转型指南
  • PCB拼板工艺:提升SMT效率与成本优化的关键技术
  • 语言驱动的视觉追踪新范式:深入解析RMOT核心框架与实战应用
  • 杰理之关机DAC未进入高阻【篇】
  • 职场选择与职业发展:半导体工程师的路径规划
  • 卷起袖子:9 个你应该探索的数据与机器学习项目示范
  • 提升效率:用快马一键生成模块化openclaw控制代码库
  • 从理论到代码:手把手教你理解Gauss-Legendre积分在MATLAB中的实现原理
  • Qwen3.5-9B零基础部署指南:开箱即用的图文对话AI,3步完成搭建
  • OpenClaw与千问3.5-35B-A3B-FP8低成本方案:自建模型接口替代OpenAI高价调用
  • OpenClaw技能组合:Qwen3.5-9B实现跨境电商多语言上架
  • 嵌入式字符LCD进度条库:LcdProgressBar轻量实现
  • 杰理之进入ANC模式播歌,ANC效果变通透【篇】
  • VSPI虚拟SPI库:GPIO模拟SPI的高精度实现与工程实践
  • NVIDIA Profile Inspector显卡优化工具全攻略:从基础设置到高级开发