Creating a Terminal shell - Redirecting Streams
This post is in continuation to to our series on creating a terminal shell. You can checkout the first post here. In this post, we’ll add the ability to redirect streams for the programs we run through our terminal shell.*
In this post we’ll add another important functionality present in almost all
terminal shells, the ability to redirect streams — stdin
, stdout
, and
stderr
.
In our shell, we’ll identify three specific tokens that will tell us that the user wants to redirect streams :
“>” — Redirect the output stream
“<” — Redirect the input stream
“>&” — Redirect the error stream
There is an easy way to redirect streams on Linux, we can use the dup2
function call which takes two arguments, the source file number, and the
destination file number :
int dup2(int oldfd, int newfd);
So, let’s say we want to redirect the output stream, what we can do is, create a
new file in write mode, call dup2
with the oldfd
as our newly created file,
and the newfd
as stdout
. So, by doing this we’re in effect making our newly
created file as stdout.
See the following example :
FILE* stdout_redirect = fopen(output_file_name, "w");
dup2(fileno(stdout_redirect), fileno(stdout));
fclose(stdout_redirect);
We need to call fclose
because now stdout
is pointing towards our file and
we can release the stdout_redirect
file descriptor.
Using this process we can create a function set_streams
which accepts a
struct
of three different streams and just redirects each of them.
Now the other problem we need to solve is to get the file names for this
redirection to take place, whenever a user would want to redirect the streams,
they would have to use those symbols we earlier mentioned. We can use theme
symbols in our tokenize_line
function. Before that we’ll create an enum and a
struct which we’ll rely on while detecting streams. We’ll add these to shell.h
header file.
typedef enum {
INPUT_STREAM,
OUTPUT_STREAM,
ERR_STREAM,
NONE_STREAM
} STREAM_TYPE;
typedef struct {
const char* input;
const char* output;
const char* err;
} stream_s;
Now whenever we get one of the tokens from [“>”, “<”, “>&”]
, we’ll set the
STREAM_TYPE
, and when we get the next token from the input line, we’ll know
that it is the file name for redirection and not a simple token.
Here’s how it’s done:
Now just run make, and we can redirect streams !
kartik@kt:~/projects/blog$ ./bin/msh
msh >> ls
bin include lib LICENSE Makefile README src
msh >> ls > out
msh >> cat out
bin
include
lib
LICENSE
Makefile
out
README
src