You need to enable JavaScript to run this app. C语言实现POSIX cat工具出现管道读取、stdin处理异常求助
优惠活动
大模型
产品
解决方案
定价
更多

C语言实现POSIX cat工具出现管道读取、stdin处理异常求助

问题原因

你的代码核心问题是错误依赖fstat返回的st_size作为读取缓冲区大小和单次读取长度,这个值仅对普通文件有效,管道、终端等流式设备的st_size无实际意义,通常为0,直接导致两个故障:

  • 管道场景下st_size为0,read每次请求读取0字节直接返回0,不会读取任何管道数据
  • 当stdin对应终端等流式设备时,0长度的读取请求不会触发实际的IO操作,导致逻辑异常

其次你的读写逻辑存在错误:不管read实际返回多少有效字节,都固定写入fs长度的数据,会输出垃圾内容,同时缺少open失败的错误处理。


修正方案

  1. 不要使用st_size动态分配缓冲区,直接使用固定大小的缓冲区(通常取页大小4096即可,兼顾效率和内存占用)
  2. 每次读取固定大小的块,按实际读到的字节数写入标准输出,直到读到EOF(read返回0)
  3. 补充错误处理逻辑,处理文件打开失败的场景
  4. 无参数场景下,外层循环只会执行一次处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 17:36:05