So I have implemented a multi-pipelined shell in C. Everything seems to work just fine as far as I have test. I built it based off of this Github Example
Now I am trying to implement I/O Redirection inside of my pipelined commands but I am unsure on how to accomplish this task. The book I am reading as well does not go into depth of C implementation of a multi-pipelined shell.
Suppose I want to give a command such as:
ls -l | grep something_i_want_to_find | wc > output.txt
and perhaps with appending as well
ls -l | grep something_i_want_to_find | wc >> output.txt
My pipeline will fail at the point of the 3rd command when it hits the redirection operator.
I was reading this SO Link as well. One of the comments in the first answer mentions "to redirect to a file, add to the last stage: if (file_fd != 1) { dup2(file_fd, 1); close(file_fd); }
" I see where they are wanting to go with this, but I have no clue how I would implement this around my pipeline code.
Here is my code:
Structure to hold command tokens
struct command
{
char **argv;
};
Pipline Function
void pipeline(struct command *cmds, int numPipes)
{
pid_t pid;
int status;
int fd[2]; // Each pipe has 2 file descriptors
int i;
int in = STDIN_FILENO;
/* Loop for number of pipes */
for (i = 0; i < numPipes; i++)
{
int fd[2];
pid_t pid; // Child's pid
if (pipe(fd) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
else if ((pid = fork()) == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) // Child
{
closeFD(fd[0]);
redirect(in, STDIN_FILENO);
redirect(fd[1], STDOUT_FILENO);
execvp(cmds[i].argv[0], (char * const *)cmds[i].argv);
}
else // Parent
{
assert(pid > 0);
close(fd[1]);
close(in);
in = fd[0];
}
}
// CODE FROM SO LINK - if (file_fd != 1) { dup2(file_fd, 1); close(file_fd); }
redirect(in, STDIN_FILENO);
redirect(STDOUT_FILENO,STDOUT_FILENO);
for (i = 0; i < numPipes; i++)
{
wait(NULL);
}
/* Execute the last stage with the current process. */
execvp (cmds[i].argv[0], (char * const *)cmds[i].argv);
}
Function to close FD's
void closeFD(int fd)
{
if ((close(fd)) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
}
Function to Redirect FD's
void redirect (int oldfd, int newfd)
{
if (oldfd != newfd)
{
if (dup2(oldfd, newfd) != -1)
{
if ((close(oldfd)) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
}
else
{
perror("dup2");
exit(EXIT_FAILURE);
}
}
}
My output looks like this when I type in ls -l | grep driver | wc > output.txt
ls -l | grep driver | wc > output.txt
Command: ls
Options: -l
Pipe
Command: grep
Arguments: driver
Pipe
Command: wc
File Redirection: >
File: output.txt
wc: >: open: No such file or directory
1 3 25 output.txt
1 3 25 total
As you can see, I am recognizing the redirection symbol but this was done in a parser in some different function, which I can provide if need be. The redirection symbol is indeed stored in the struct command **cmds
vector. How could I accomplish having I/O redirection inside of my pipeline?