将stdout捕获到字符串并将其输出回C中的stdout

时间:2012-06-26 19:20:35

标签: c redirect stdout

使用C语言。我有一个写入stdout的函数。

我想捕获该输出,稍微修改一下(替换一些字符串)。然后再将它输出到标准输出。所以我想从:

开始
char huge_string_buf[MASSIVE_SIZE];
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */

现在的问题是,如何将huge_string_buffer输出回原来的标准输出?

5 个答案:

答案 0 :(得分:2)

一个想法是模仿标准Unix实用程序tee的功能,但完全在您的程序中完成,而不依赖于外部重定向。

所以我编写了一个简单的函数mytee(),它似乎有效。它使用shmget(), pipe(), fork(), and dup2()

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>

static char *mytee(int size) {
   int shmid = shmget(IPC_PRIVATE, size + 1, 0660 | IPC_CREAT);
   int pipe_fds[2];
   pipe(pipe_fds);

   switch (fork()) {
      case -1:                      // = error
         perror("fork");
         exit(EXIT_FAILURE);
      case 0: {                     // = child
         char *out = shmat(shmid, 0, 0), c;
         int i = 0;
         out[0] = 0;
         dup2(pipe_fds[0], 0);      // redirect pipe to child's stdin
         setvbuf(stdout, 0, _IONBF, 0);
         while (read(0, &c, 1) == 1 && i < size) {
            printf("<%c>", c);      // pass parent's stdout to real stdout,
            out[i++] = c;           // and then buffer in mycapture buffer
            out[i] = 0;             // (the extra <> are just for clarity)
         }
         _exit(EXIT_SUCCESS);
      }
      default:                      // = parent
         dup2(pipe_fds[1], 1);      // replace stdout with output to child
         setvbuf(stdout, 0, _IONBF, 0);
         return shmat(shmid, 0, 0); // return the child's capture buffer
   }
}

我的测试程序是:

int main(void) {
   char *mycapture = mytee(100);    // capture first 100 bytes
   printf("Hello World");           // sample test string
   sleep(1);
   fprintf(stderr, "\nCaptured: <%s>\n", mycapture);
   return 0;
}

输出结果为:

<H><e><l><l><o>< ><W><o><r><l><d>
Captured: <Hello World>

要在您的应用程序中使用此功能,在mytee()中,您需要使用printf("<%c>", c)替换测试语句write(1, &c, 1)。您可能需要在调用read时处理信号。在两个dup2()中的每一个之后,您可能想要添加:

  close(pipe_fds[0]);
  close(pipe_fds[1]);

有关此类内容的参考资料,请参阅Dave的优秀和简短的27页58页O'Reilly book 在Unix系统上使用C 咖喱。

答案 1 :(得分:1)

Unix的方法实际上只是编写一个程序来完成你需要的输入处理,然后在命令行上将其他程序的输出传递给它。

如果你坚持要在C程序中保留所有内容,那么我要做的就是重写该函数,让它将其输出发送到给定的char缓冲区(最好是return缓冲区的char * }),以便它可以发送到stdout或按照客户端的需要进行处理。

例如,旧的方式:

void usage () {
    printf ("usage: frob sourcefile [-options]\n");
}

......和新的方式:

char * usage(char * buffer) {
    strcpy (buffer, "usage: frob sourcefile [-options]\n");
    return buffer;
}

答案 2 :(得分:0)

我真的不喜欢带文件描述符的棘手游戏。你不能修改这个函数,以便除了写入stdout之外还以其他方式返回数据吗?

如果您无法访问源代码,并且您无法访问源代码,那么我建议将写入stdout的代码分解为一个小的单独程序,并将其作为另一个进程运行。从进程重定向输出(可能通过命名管道)是简单而干净的,然后从接收数据的进程输出到stdout就没有问题。

此外,根据您希望进行的编辑类型,最好使用Python等高级语言来编辑数据。

答案 3 :(得分:0)

Python有很好的方法来完成你想要的东西。但是如果你真的需要使用C语言,那么我会用C语言读一些管道。但是要小心,它们可以非常快速地复杂化。

答案 4 :(得分:0)

char huge_string_buf[MASSIVE_SIZE];
FILE stdout_ori=fdopen(stdout,"a");
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
//Write to stdout_fdopen