This short post is a recount of an exploration into redirection in the C language.
As always, let's go to our friend Wikipedia to set the definition for us:
In computing, redirection is a form of interprocess communication, and is a function common to most command-line interpreters, including the various Unix shells that can redirect standard streams to user-specified locations.
In Unix-like operating systems, programs do redirection with the dup2(2) system call, or its less-flexible but higher-level stdio analogues, freopen(3) and popen(3).
Basic redirection can use <
to redirect input and >
to redirect output.
For example, we can use the redirect output operator to redirect the output from echo "Hello!"
into a file example.txt
.
> echo "Hello!" > example.txt
> cat example.txt
Hello!
As mentioned by our pal Wikipedia, we can use the dup2
system call in C to manage a similar thing!
A simple example
In our first example, we are going to write a simple example of two variables that open a foobar.txt
that iterates character by character.
touch foobar.txt one.c
Inside of foobar.txt
, add th following:
foobar test
As for the contents of one.c
:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main() {
int fd1, fd2;
char c;
fd1 = open("foobar.txt", O_RDONLY);
fd2 = open("foobar.txt", O_RDONLY);
// c becomes f
read(fd2, &c, 1);
// c becomes o
read(fd2, &c, 1);
// c becomes o
read(fd2, &c, 1);
// c becomes b
read(fd2, &c, 1);
printf("c = %c\n", c); // c = b
// now reading in fd1, so c becomes f again
read(fd1, &c, 1);
printf("c = %c\n", c); // c = f
// redirect and now fd2 is now back at f
dup2(fd1, fd2);
// reading back fd2 which has been redirected,
// so c actually becomes o!
read(fd2, &c, 1);
printf("c = %c\n", c); // c = o
exit(0);
}
The comments in the code explain what is happening in order, but we're just going to print out the result by running gcc one.c && ./a.out
. The output binary a.out
is the name given since we do not provide output to GCC.
> gcc one.c && ./a.out
c = b
c = f
c = o
To explain further what is going on:
- We assign
fd1
andfd2
to openfoobar.txt
. - We use
read
to read in a character and assign it to variablec
. - Each time we read
fd2
, we move one character further along in the text file. - Eventually, we read
fd1
once and then we redirectfd1
tofd2
. - We read
fd2
one last time, but after redirection the value now reads "o".
A more readable example
The above can seem hard to comprehend - it is better playing around with this stuff in C. This example, I decided to use scanf
to read in from stdin in the second example, because I feel like the example was a little clearer for me.
Note: Given I knew the length of the words in the file, I just set a max STR_LEN of 6 as opposed to some dynamic calculation.
Create a file two.c
.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define STR_LEN 6
int main() {
int fd1;
char *c1 = (char*)malloc(STR_LEN);
char *c2 = (char*)malloc(STR_LEN);
// notice no values
printf("c1 = %s\nc2 = %s\n", c1, c2);
fd1 = open("foobar.txt", O_RDONLY);
// redirect fd1 to stdin
if (dup2(fd1, STDIN_FILENO) < 0) {
printf("Unable to duplicate file descriptor.");
exit(EXIT_FAILURE);
}
// scan c1 and c2 in from stdin
scanf("%s %s", c1, c2);
// values now becomes "foobar" and "test" respectively
printf("c1 = %s\nc2 = %s\n", c1, c2);
// SAVE THE WHALES, FREE THE MALLOCS
free(c1);
free(c2);
exit(0);
}
Now if we run gcc two.c && ./a.out
, we get the following:
> gcc two.c && ./a.out
c1 =
c2 =
c1 = foobar
c2 = test
In this case, we do the following:
- Allocate memory for
c1
andc2
. - Confirm no values in first print.
- Read in the
foobar.txt
file to file descriptorfd1
. - Redirect
fd1
tostdin
. - Use
scanf
to assign the values toc1
andc2
fromstdin
. - Confirm with the last print that
c1
andc2
have been assigned the words "foobar" and "test" respectively!
Hooray! Redirection to stdin is a success (and no segmentation faults).
I will likely redo this exercise in Rust and Golang this week to show the how-to.
Resources
- Computer Systems A Programmer's Perspective - Page 944
- dup2 System Call
- Stack Overflow - difference between read and fread
- Unix System Calls - read
- Top 20 C Pointer Mistakes
- Stack Overflow - Reading file and getting string length
- CS 702 Operating Systems - redirect and pipes
- TutorialsPoint - Redirection
- Redirection - Wikipedia
Image credit: Michael Kubler
Originally posted on my blog. Follow me on Twitter for more hidden gems @dennisokeeffe92.
Top comments (0)