menu XUJINKAI 的个人主页
create settings
home
主页
list
全部博文
  • Collection
  • About
  • assistant_photo
    个人项目
    person
    关于

    Linux C 重定向标准输出到文件或内存

    Tags: Linux , C

    先看示例

    以下示例将标准输出重定向到一块内存区域,结束时,可以获取这块内容,方便单元测试。

    void *m = fd_redirect_to_memory_begin(STDOUT_FILENO); // STDOUT_FILENO等于1
    printf("123");
    puts("456");
    char *result = fd_redirect_to_memory_end(m);
    // result 这里就等于"123456\n"
    free(result);
    

    也可以将标准输出重定向到文件,这样,程序启动时设置一下,就可以将所有屏幕日志导向文件。

    FILE *fp = fopen(LOG_FILE_NAME, "w+");
    void *m = fd_redirect_to_file_begin(STDOUT_FILENO, fp);
    printf("123");
    puts("456");
    fd_redirect_to_file_end(m);
    // 文件中的内容就等于"123456\n"
    fclose(fp);
    

    如何实现

    标准输出的重定向主要使用dup和dup2函数:

    int dup(int oldfd);
    int dup2(int oldfd, int newfd);
    

    dup函数将创建一个新的文件描述符,指向与oldfd相同的文件。

    dup2函数会让newfd指向与oldfd相同的文件。

    所以,可以先将STDOUT_FILENO通过dup函数复制一份,再使用dup2让STDOUT_FILENO指向新的位置。

    核心代码如下:

    // 1就是STDOUT_FILENO
    // 开始重定向
    int backupfd = dup(1); // backupfd会等于(比如)3,指向与stdout相同的位置
    dup2(your_own_fd, 1);  // 将1号fd重定向到别的文件
    // 结束重定向
    dup2(backupfd, 1);     // 将1号fd恢复
    close(backupfd)        // 别忘了关闭复制出来的fd
    

    完整代码

    fd_redirect_to_file

    重定向到文件的代码比较简单,需要注意fileno(fp)不一定是有效的fd就可以了。

    struct redirect_file_st
    {
        int fno;
        int dup;
    };
    
    void *fd_redirect_to_file_begin(int __fileno, FILE *fp)
    {
        struct redirect_file_st *__st = malloc(sizeof(struct redirect_file_st));
        __st->fno = __fileno;
        __st->dup = dup(__fileno);
        if (__st->dup < 0)
        {
            free(__st);
            return NULL;
        }
        if (dup2(fileno(fp), __fileno) < 0) // fileno(fp) may not a fd
        {
            close(__st->dup);
            free(__st);
            return NULL;
        }
        return __st;
    }
    
    void fd_redirect_to_file_end(void *p)
    {
        if (p == NULL)
        {
            fprintf(stderr, "fd_redirect_to_file_end: input ptr cannot be NULL.");
            abort();
        }
        struct redirect_file_st *__st = p;
        dup2(__st->dup, __st->fno);
        close(__st->dup);
        free(__st);
    }
    

    fd_redirect_to_memory

    重定向到内存的代码略有些不一样,是因为open_memstream打开的文件流,虽然也是FILE结构,但其实是没有fd这个东西的(因为没有真实的打开文件),所以需要借助pipe来读取。

    struct redirect_memory_st
    {
        int fno;
        int dup;
        int fd[2];
    };
    
    void *fd_redirect_to_memory_begin(int __fileno)
    {
        struct redirect_memory_st *__st = malloc(sizeof(struct redirect_memory_st));
        __st->fno = __fileno;
        __st->dup = dup(__fileno);
        if (__st->dup < 0)
        {
            free(__st);
            return NULL;
        }
        if (pipe(__st->fd) < 0)
        {
            close(__st->dup);
            free(__st);
            return NULL;
        }
        dup2(__st->fd[1], __fileno);
        return __st;
    }
    
    char *fd_redirect_to_memory_end(void *p)
    {
        if (p == NULL)
        {
            fprintf(stderr, "fd_redirect_to_memory_end: input ptr cannot be NULL.");
            abort();
        }
        struct redirect_memory_st *__st = p;
        char *result;
        size_t len;
        FILE *fp = open_memstream(&result, &len);
    
        char buf[128];
        ssize_t n;
        fcntl(__st->fd[0], F_SETFL, fcntl(__st->fd[0], F_GETFL) | O_NONBLOCK);
        while ((n = read(__st->fd[0], buf, 128)) > 0)
        {
            fwrite(buf, 1, n, fp);
        }
    
        close(__st->fd[0]);
        close(__st->fd[1]);
        dup2(__st->dup, __st->fno);
        close(__st->dup);
    
        fclose(fp);
        free(__st);
        return result;
    }
    

    Disqus评论加载中。。。