You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C程序如何获取终端重定向传递的文件描述符

如何在Linux的C程序中识别通过重定向打开的文件描述符

嘿,这个问题确实容易被忽略——毕竟shell处理的重定向不会出现在argv里,程序启动时这些文件描述符已经默默打开了。下面我给你两种实用的方法,让你的程序能自动识别所有打开的文件描述符,包括像4、5这种自定义的重定向fd。

方法一:遍历可能的文件描述符范围

Linux下进程的文件描述符是从0开始的整数,你可以遍历一个合理的范围(比如从0到sysconf(_SC_OPEN_MAX)返回的最大值),用fcntl来检查每个fd是否有效。

核心思路

  • 用fcntl(fd, F_GETFD)来判断fd是否打开:如果调用成功,说明fd是有效的;如果返回-1且errno是EBADF,说明这个fd未被打开。
  • 可以结合fstat获取更多fd的属性(比如是不是常规文件、字符设备等),帮你过滤掉不需要的fd。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

int main() {
    long max_fd = sysconf(_SC_OPEN_MAX);
    if (max_fd == -1) {
        perror("sysconf failed");
        return 1;
    }

    printf("Open file descriptors:\n");
    for (long fd = 0; fd < max_fd; fd++) {
        // 检查fd是否有效
        if (fcntl(fd, F_GETFD) == -1) {
            if (errno == EBADF) {
                continue; // 这个fd未打开
            } else {
                perror("fcntl failed");
                continue;
            }
        }

        // 获取fd对应的文件信息
        struct stat st;
        if (fstat(fd, &st) == -1) {
            perror("fstat failed");
            continue;
        }

        printf("fd %ld: ", fd);
        if (S_ISREG(st.st_mode)) {
            printf("regular file\n");
        } else if (S_ISCHR(st.st_mode)) {
            printf("character device\n");
        } else if (S_ISFIFO(st.st_mode)) {
            printf("FIFO/pipe\n");
        } else if (S_ISSOCK(st.st_mode)) {
            printf("socket\n");
        } else {
            printf("unknown type\n");
        }
    }

    return 0;
}

编译运行后,执行./sample 4</some_file 5<some_other_file,你就能看到fd 4和5被列出来啦。

方法二:利用Linux的/proc文件系统

Linux提供了/proc/self/fd目录,里面的每个条目都是符号链接,对应进程当前打开的文件描述符(文件名就是fd的数字)。读取这个目录就能直接拿到所有打开的fd,还能通过readlink获取对应的文件路径。

核心思路

  • 用opendir打开/proc/self/fd目录。
  • 用readdir遍历目录中的每个条目,跳过.和..,以及打开目录本身的fd(比如opendir返回的fd,避免死循环)。
  • 用readlink读取每个符号链接的目标,拿到文件路径。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>

int main() {
    DIR *dir = opendir("/proc/self/fd");
    if (!dir) {
        perror("opendir failed");
        return 1;
    }

    printf("Open file descriptors and their paths:\n");
    struct dirent *entry;
    int dir_fd = dirfd(dir); // 获取打开/proc/self/fd的fd,避免把它也算进去

    while ((entry = readdir(dir)) != NULL) {
        // 跳过.和..
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 把条目名转换成fd数字
        long fd = strtol(entry->d_name, NULL, 10);
        if (fd == dir_fd) {
            continue; // 跳过目录自身的fd
        }

        // 读取符号链接的目标路径
        char path[PATH_MAX];
        ssize_t len = readlink(entry->d_name, path, sizeof(path)-1);
        if (len == -1) {
            perror("readlink failed");
            continue;
        }
        path[len] = '\0';

        printf("fd %ld: %s\n", fd, path);
    }

    closedir(dir);
    return 0;
}

这种方法更直观,能直接看到每个fd对应的文件路径,非常适合你的场景。

两种方法的对比

  • 遍历法:不依赖/proc,能在其他UNIX-like系统(比如FreeBSD)上运行,但需要遍历整个fd范围,效率稍低。
  • /proc法:Linux专属,能直接获取文件路径,效率更高,但只能在Linux系统上使用。

你可以根据自己的需求选择合适的方法~

内容的提问来源于stack exchange,提问作者Luki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:26:33