Using dup2() to redirect output

Sometimes, you want to redirect the output of your program to a file–maybe to record it for another program, or because you want to search through it with grep. If you have to do this at the C level, there are a few ways you can accomplish this.

Using a variable

Perhaps the simplest way to do this is to use the fprintf family of functions along with a variable that controls what file we want to output to. For example, we can do the following:

  const char* filename = get_filename_or_null(); // NULL means "use stdout"
  FILE* outStream;

  if(filename){
    FILE* out = fopen(filename, "w");
    if(out){
      outStream = out;
    } else {
      outStream = stdout;
    }
  } else {
    outStream = stdout;
  }

   // Filler code here
   fprintf(outStream, "my fancy output");

Note that this is a little annoying because we have to specify outStream whenever we want to print. What if we wanted to avoid this variable shenaniganery?

Modifying stdout

It turns out that on many (though not all!) systems, printf is defined to be something like this:

int printf(const char* format, other_args...){
  fprintf(stdout, format, other_args...);
}

Don’t worry too much about the other_args stuff for now. Notice that printf is just fprintf with a FILE* variable called stdout. This suggests that if we overrode the stdout variable, we could get all usages of printf to print to a file.

Let’s try it out:

  const char* filename = get_filename_or_null();
  if(filename){
    FILE* outputFile = open(filename, "w");
    if(outputFile){
      stdout = outputFile;
    }
  }
  // Filler code here
  printf("my fancy output");

Does this work on your system? It works on mine…but this is wildly unsafe to rely on!. All it takes is a different implementation of printf, and output redirection will stop working.

These two methods also have the disadvantage that, if you fork-and-exec, they stop working. Let’s take a look at a method that gets rid of all these issues.

Using dup2()

We can use dup2() to duplicate a file descriptor, which will allow us to redirect output with the following code snippet:

  const char* filename = get_filename_or_null();
  if(filename){
    int fd = open(filename, O_WRONLY, 0666);
    dup2(fd, STDOUT_FILENO); // Check `man stdin` for more info
    dup2(fd, STDERR_FILENO);
    // Check the return values of dup2 here
  }

  // Filler code here
  printf("My fancy output");

Notice that this code essentially does the same thing as previously: it redirects output to a file. However, there’s a very important difference here. If the process forks-and-execs, the child process will still have its output redirected to file, even after the exec!. This is something that was not true of our earlier solutions, and is a very useful property of the dup2() solution.

Written August 30, 2020 • Last modified January 16, 2021