This project aims to simulate the pipe operator in bash.
The pipe operator is used when we need an interaction between commands. For example, the command
< file1 cat | head -5 > file2
means “execute the cat
function with the file file1
as input then the head
function with the result of previous command as input and output the result in the file file2
.”
The pipex program is called like this:
./pipex file1 cmd1 cmd2 file2
In order to achieve this project, we will need to use multiple processes. To create a new process, the function fork()
is used in C. Each command is run in a new process and processes communicate thanks to pipes, created with the pipe(fd[2])
function.
For the first command, the input is file1
. It means that we must open the file and redirect the standard input in the new process to the file descriptor of file1
. The standard output will be redirected to the write end of the pipe (fd[1]
).
The second command will take the read end of the pipe (fd[0]
) and the file2
as standard output.
There are 2 bonuses in this project.
The first bonus is about handling multiple commands as such:
./pipex infile cmd1 cmd2 cmd3 ... outfile
In order to achieve that goal, I use 2 pipes one after another to provide an input and an output to each command. The difficult part is to correctly handle pipes opening and closing at the good time.
The second bonus is about handling the “here doc”. Here doc operator is <<
and allows to get an input of multiple lines until a defined delimiter is written to the input. The program must behave like the command
cmd << END | cmd2 >> outfile
In this command, the first command has as input the standard input and we can write anything, including newlines, until we write “END”. Our program will be called like this:
./pipex here_doc DELIMITER cmd1 cmd2 outfile
Handling this bonus is quite easy because we just need to use our get_next_line(fd)
function and write each line to the first input pipe until the delimiter is found.
To handle all the bonuses, here are the most important parts of my program sequence:
- If the input is the here_doc, create a first pipe (that we will call pipe_in) and get the stdin with
get_next_line
and write lines to the pipe - Create a pipe for the output (pipe_out)
- Create a new process (the first)
- In this new process, if the input is a file, then open it
- Redirect stdin to the pipe_in or the infile
- Redirect stdout to the pipe_out
- Run the command
- For all the commands before the last:
- Close the last pipe_in
- Create a new pipe as pipe_out
- Create a new process in which stdin is redirected to pipe_in and stdout to pipe_out then run the command
- For the last command:
- Close the last pipe_in
- Open the outfile
- Create a new process in wich stdin is redirected to pipe_in and stdout to outfile
- Exit the programm with the last process exit code.
To run a command, execve()
is used. This function needs the absolute path to the command, the list of the options provided with the command (as an array of strings) and the environment array. Since the commands can be written without their absolute path, we need to find it. In order to do that, We need to find the path variable in the env array and try to run execve with it. If the function returns anything, it means it failed so we try with the next path until the function succeeds.