Capture stdout to a string and output it back to stdout in C

怎甘沉沦 提交于 2019-12-07 18:52:02

问题


C language is used. I have a function that writes to stdout.

I would like to capture that output, modify it a bit (replacing some strings). And than output it again to the stdout. So I want to start with:

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 */

The question is now, how do I output the huge_string_buffer back to the original stdout?


回答1:


One idea is to mimic the functionality of the standard Unix utility tee, but to do so entirely within your program, without relying on outside redirection.

So I've written a simple function, mytee(), which seems to work. It uses 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
   }
}

My test program is:

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;
}

The output is:

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

To use this in your application, in mytee() you'll need to replace the test statement printf("<%c>", c) with just write(1, &c, 1). And you may need to handle signals in the call to read. And after each of the two dup2()'s, you may want to add:

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

For a reference on this sort of stuff, see for example the excellent and short 27-year-old 220-page O'Reilly book Using C on the Unix System by Dave Curry.




回答2:


The Unix way to do this is really to just write a little program that does the input processing you need, and then pipe the output of that other program to it on the command line.

If you insist on keeping it all in your C program, what I'd do instead is rewrite that function to have it send its output to a given char buffer (preferably returning the buffer's char *), so that it can be sent to stdout or processed as the client desires.

For example, the old way:

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

...and the new way:

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



回答3:


I really don't like tricky games with file descriptors. Can't you modify the function so that it returns its data some other way than by writing to stdout?

If you don't have access to the source code, and you can't do that, then I would suggest breaking out the code that writes to stdout into a small separate program, and run that as another process. It is easy and clean to redirect output from a process (maybe through a named pipe), and then you will have no problem with outputting to stdout from the process that receives the data.

Also, depending on the sort of editing you wish to do, you might be better off using a high-level language like Python to edit the data.




回答4:


Python has really good methods for accomplishing what you want. But if you really need to use C, then I would do some reading into pipes in the C language.. Be careful though, they can get really complicated very fast.




回答5:


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


来源:https://stackoverflow.com/questions/11214498/capture-stdout-to-a-string-and-output-it-back-to-stdout-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!