C语言实现POSIX cat工具出现管道读取、stdin处理异常求助
问题原因
你的代码核心问题是错误依赖fstat返回的st_size作为读取缓冲区大小和单次读取长度,这个值仅对普通文件有效,管道、终端等流式设备的st_size无实际意义,通常为0,直接导致两个故障:
- 管道场景下
st_size为0,read每次请求读取0字节直接返回0,不会读取任何管道数据 - 当
stdin对应终端等流式设备时,0长度的读取请求不会触发实际的IO操作,导致逻辑异常
其次你的读写逻辑存在错误:不管read实际返回多少有效字节,都固定写入fs长度的数据,会输出垃圾内容,同时缺少open失败的错误处理。
修正方案
- 不要使用
st_size动态分配缓冲区,直接使用固定大小的缓冲区(通常取页大小4096即可,兼顾效率和内存占用) - 每次读取固定大小的块,按实际读到的字节数写入标准输出,直到读到EOF(
read返回0) - 补充错误处理逻辑,处理文件打开失败的场景
- 无参数场景下,外层循环只会执行一次处理
stdin,内层循环持续读直到EOF即可符合要求:重定向文件时读完自动退出,终端交互时直到用户输入Ctrl+D(EOF)或终止信号才退出
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> // 定义缓冲区大小为4KB,和系统页大小一致 #define BUF_SIZE 4096 int main(int argc, char *argv[]) { char opt; while ((opt = getopt(argc, argv, "u")) != EOF) { switch(opt) { case 'u': /* Make the output un-buffered */ setbuf(stdout, NULL); break; default: fprintf(stderr, "pcat: 不支持的选项 %c\n", opt); exit(EXIT_FAILURE); } } argc -= optind; argv += optind; int i = 0, fildes; char buf[BUF_SIZE]; // 栈上分配固定大小缓冲区,不需要动态申请释放 do { /* Check for operands, if none or operand = "-". Read from stdin */ if (argc == 0 || !strcmp(argv[i], "-")) { fildes = STDIN_FILENO; } else { fildes = open(argv[i], O_RDONLY); if (fildes == -1) { fprintf(stderr, "pcat: %s: %s\n", argv[i], strerror(errno)); i++; continue; } } /* Check for directories */ struct stat fb; if (!fstat(fildes, &fb) && S_ISDIR(fb.st_mode)) { fprintf(stderr, "pcat: %s: Is a directory\n", argv[i]); if (fildes != STDIN_FILENO) close(fildes); i++; continue; } ssize_t n_read; // 循环读直到EOF或错误 while ((n_read = read(fildes, buf, BUF_SIZE)) > 0) { ssize_t n_written = 0; // 处理部分写的场景,确保所有读到的字节都被写出 while (n_written < n_read) { ssize_t res = write(STDOUT_FILENO, buf + n_written, n_read - n_written); if (res == -1) { perror("pcat: 写入标准输出失败"); exit(EXIT_FAILURE); } n_written += res; } } if (n_read == -1) { perror("pcat: 读取失败"); exit(EXIT_FAILURE); } /* Close file if it's not stdin */ if (fildes != STDIN_FILENO) close(fildes); i++; } while (i < argc); return 0; }
内容的提问来源于stack exchange,提问作者user419050
相关产品推荐
相关产品推荐

