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:
Due to issues with anti-malware scanners on the UTCS servers, certain strings will get this blog post flagged as malicious and blocked. To prevent this, certain lines below have been modified with invisible emoji characters.
While this prevents the antimalware block, it will cause code breakage if you attempt to copy-paste the entire code block. You must manually retype the line indicated by the comment below, or your code will break with a bewildering message when you attempt to compile it.
// NULL means "use stdout"
const char* filename = get_filename_or_null();
FILE* outStream;
if(filename){
// You must manually retype the line below or things will break!
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.