 Learn about Linux in practice  1. Overview
Most Linux system commands are built around a simple flow: the command reads from the standard input and writes to the standard output. This arrangement is very rudimentary but also extremely flexible when combined with input-output redirection. As a result, we can perform arbitrary operations on files.
In this tutorial, we’ll learn how to perform input-output redirection within a C program.
2. Linux Redirection Basics
In the Linux command line, two operators govern redirection: < for input and > for output.
For instance, let’s consider the cat command. It reads input and prints it on the screen when Return is encountered:
$ cat
This line will be repeated
This line will be repeated
^C
If we want to send input to a file, we can redirect the output:
$ cat > output_file
It will go to the file
Now, let’s redirect output_file to the input of cat, which means cat should print it in the terminal:
$ cat < output_file
It will go to the file
Thus, we populate a file through standard input and produce its contents via standard output.
3. C File Descriptors
With C, we access files using file descriptors. For a secondary storage file, we can obtain a descriptor with the open system call. The descriptor is an integer variable, set by open to the lowest available value. A function that operates on file descriptors uses them to find the file metadata, complete information about the corresponding file or device.
We use special, predefined values for standard streams:
- standard input (STDIN): 0 (zero)
- standard output (STDOUT): 1 (one)
- standard error (STDERR): 2 (two)
Armed with this basic information, we should be ready to understand file descriptor duplication.
4. The dup2() Function
The dup2() function duplicates a file descriptor:
int dup2(int oldfd, int newfd)
When the function returns, newfd refers to the file or device indicated by oldfd before. Consequently, the file pointed to by oldfd is closed. So, if the old file descriptor points to standard output and the new one to a file, the file should now act as the standard output. Any C function or Linux command called inside a C program and designed to write to STDOUT should now output to that file.
Critically, we achieve this duplication and redirection without any modification to the function code.
Finally, dup2() returns the new file descriptor on success and -1 on failure.
5. Examples
Let’s examine a few examples to get familiar with the dup2() function. For the sake of simplicity and clarity, we skip error checking after function calls.
5.1. Redirecting to File
In this first program, example_1.c, we redirect the printf command output to the user-provided file:
#include <stdlib.h> /* for exit */
#include <stdio.h> /* for printf and stderr */
#include <fcntl.h> /* for open */
#include <unistd.h> /* for dup2 */
#define STDOUT 1
int main(int argc, char **argv)
{
int file_desc;
char *output_file_name;
if (argc != 2)
{
fprintf(stderr, "Provide output filename, correct use: %s output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
output_file_name = argv[1];
file_desc = open(output_file_name,
O_CREAT | O_TRUNC | O_WRONLY, /* create, trunc, only write to */
S_IRUSR | S_IWUSR); /* user can read and write, if newly created */
printf("Printing to standard output before redirection \n");
printf("Switching standard output to file \"%s\".\n", output_file_name);
dup2(file_desc, STDOUT);
printf("Printing to standard output after redirection.\n");
exit(EXIT_SUCCESS);
}
In this case, the output filename comes from the command line, stored in argv[1]. Then, we create or open the corresponding file, saving its descriptor in the file_desc variable. Finally, with the dup2() call, we replace STDOUT with this variable.
Let’s compile the program:
$ gcc example_1.c
Then, we run it, using the default program name, a.out:
$ ./a.out test_output_file
Printing to standard output before redirection
Switching standard output to file "test_output_file".
Finally, let’s check the output file:
$ cat test_output_file
Printing to standard output after redirection.
As expected, we see that the specified file contains the expected data.
5.2. Overwriting Current Process
In the next example, we start the uname command to provide the system name and details. Let’s study example_2.c code:
#include <stdlib.h> /* for exit */
#include <stdio.h> /* for printf and stderr */
#include <fcntl.h> /* for open */
#include <unistd.h> /* for dup2 */
#define STDOUT 1
int main(int argc, char **argv)
{
int file_desc;
char *output_file_name;
char *cmd[] = { "/usr/bin/uname", "-a", 0 };
if (argc != 2)
{
fprintf(stderr, "Provide output filename, correct use: %s output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
output_file_name = argv[1];
file_desc = open(output_file_name,
O_CREAT | O_TRUNC | O_WRONLY, /* create, trunc, only write to */
S_IRUSR | S_IWUSR); /* user can read and write, if newly created */
printf("Output of command %s will be redirected to file \"%s\"\n", cmd[0], output_file_name);
dup2(file_desc, STDOUT);
execvp(cmd[0], cmd);
exit(EXIT_FAILURE); /* get here only if error in execvp */
}
First, we provide a Linux shell command with arguments as an array of strings in the cmd variable. Then, we create a file and substitute the standard output with it. Afterward, we start the uname command using the execvp() function. Thus, the command should run in a new process, an image of the current one. So, the output substitution holds for this process too.
Now, let’s compile the program:
$ gcc example_1.c
Afterward, we can start it and examine the result:
$ ./a.out test_output_file
Output of command /usr/bin/uname will be redirected to file "test_output_file"
$ cat test_output_file
Linux ubuntu 6.8.0-59-generic #61~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 15 17:03:15 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
Of course, the redirection works in this case as well.
5.3. Child Process for Redirection
The successful call of execvp() ends the current process. Now, we want to preserve the current process, so we use the fork() function to create a child process.
#include <stdlib.h> /* for exit */
#include <stdio.h> /* for printf and stderr */
#include <fcntl.h> /* for open */
#include <unistd.h> /* for dup2 */
#include <sys/wait.h> /* for wait */
#define STDOUT 1
/* child process function prototype */
void run_cmd(int fd, char **cmd);
int main(int argc, char **argv)
{
int file_desc;
char *output_file_name;
char *cmd[] = { "/usr/bin/uname", "-a", 0 };
if (argc != 2)
{
fprintf(stderr, "Provide output filename, correct use: %s output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
output_file_name = argv[1];
file_desc = open(output_file_name,
O_CREAT | O_TRUNC | O_WRONLY, /* create, trunc, only write to */
S_IRUSR | S_IWUSR); /* user can read and write, if newly created */
run_cmd(file_desc, cmd);
printf("We're finished!\n");
exit(EXIT_SUCCESS);
}
/* Function to fork and execute in a new thread */
void run_cmd(int fd, char **cmd)
{
int status;
switch (fork()) {
case 0: /* child process */
dup2(fd, STDOUT);
execvp(cmd[0], cmd);
exit(EXIT_FAILURE); /* get here only if error in execvp */
default: /* parent process */
while (wait(&status) != -1) ; /* wait for child to be finished */
printf("Child process finished\n");
break;
case -1: /* fork error, skip handling it */
}
return;
}
The run_cmd function creates a new process with fork() and takes appropriate action according to its result. Notably, the code of the run_cmd function is executed in both the child and the parent processes. The return value of fork() is zero, indicating the child process. In this case, we start a new process with changed file descriptors, as in the last example. In the parent process, we wait for the child with the wait() system call.
Let’s compile and test the program:
$ gcc example_3.c
$ ./a.out test_output_file
Child process finished
We can see that the file descriptor duplication has a process scope. The child dup2() call doesn’t affect the parent’s standard output, and the Child process finished message appears in the terminal.
6. Restoring Standard Output
Now that we’ve gotten to know different redirections, let’s revisit the first example and switch back the standard output to the terminal. Let’s check example_4.c:
#include <stdlib.h> /* for exit */
#include <stdio.h> /* for printf and stderr */
#include <fcntl.h> /* for open */
#include <unistd.h> /* for dup2 */
#define STDOUT 1
int main(int argc, char **argv)
{
int file_desc;
char *output_file_name;
if (argc != 2)
{
fprintf(stderr, "Provide output filename, correct use: %s output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
output_file_name = argv[1];
file_desc = open(output_file_name,
O_CREAT | O_TRUNC | O_WRONLY, /* create, trunc, only write to */
S_IRUSR | S_IWUSR); /* user can read and write, if newly created */
printf("Printing to standard output before redirection \n");
fflush(stdout);
int saved_stdout = dup(STDOUT); /* save standard output description for later */
printf("Switching standard output to file \"%s\".\n", output_file_name);
dup2(file_desc, STDOUT);
printf("Printing to standard output after redirection.\n");
dup2(saved_stdout, STDOUT); /* copy saved STDOUT to the current one. Ovewrite previous redirection */
printf("The standard output is terminal again!\n");
exit(EXIT_SUCCESS);
}
We use dup(), a sister function of dup2(). This function creates a copy of the file description and enables both files to be operated on simultaneously. Finally, we use dup2() to switch STDOUT back to standard output.
Let’s verify the result:
$ gcc example_1a.c
$ ./a.out test_output_file
Printing to standard output before redirection
Switching standard output to file "test_output_file".
The standard output is terminal again!
This way, we can restore the original STDOUT.
7. Conclusion
In this article, we learned about output redirection in C.
First, we recalled Linux redirection basics. Then, we took a brief look at how C programs dealt with file descriptors and introduced the dup2() function. Later, we moved to examples of the output redirection in C, substituting a file for the standard output and even redirecting the output of the Linux command uname called from inside the C program. The post Redirecting Standard Output in C With the dup2() Function first appeared on Baeldung on Linux.
Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative. |