The PIPEX project from 42 aims to recreate the behavior of the shell pipeline operator (|). In other words, you will create a program that links the output of one command to the input of another, just like when using the terminal command:
ls -l | grep "txt"Create a C program that executes:
./pipex file1 "cmd1" "cmd2" file2This should replicate the following shell command:
< file1 cmd1 | cmd2 > file2In other words:
1️⃣ Read data from file1
2️⃣ Execute cmd1 using file1 as input
3️⃣ Pass the output of cmd1 as input to cmd2
4️⃣ Save the final result in file2
- Use
open()to openfile1(read) andfile2(write).
- Use
pipe()to create a communication channel between processes.
- Use
fork()to create two child processes.
- The first process redirects
file1to the command input. - The second process redirects the output of the first command to the input of the second.
- Use
execve()to replace the current process with the desired command.
- Use
close()andwaitpid()to ensure everything finishes correctly.
Opens files for reading or writing. It returns a file descriptor or -1 if an error occurs.
int fd = open("file.txt", O_RDONLY);
if (fd == -1) {
perror("open failed");
}Creates a unidirectional data channel between two processes. It returns 0 on success and -1 on failure.
int fd[2];
if (pipe(fd) == -1) {
perror("pipe failed");
}Creates a new child process. Returns 0 in the child process and the child's PID in the parent.
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
}Duplicates a file descriptor to another, redirecting standard input or output.
int fd = open("input.txt", O_RDONLY);
dup2(fd, STDIN_FILENO);
close(fd);Replaces the current process with a new program. It requires the full path of the executable and an argument list.
char *args[] = {"/bin/ls", "-l", NULL};
execve(args[0], args, envp);
perror("execve failed");Closes a file descriptor, releasing system resources.
close(fd);Waits for a specific child process to terminate, ensuring proper process synchronization.
int status;
pid_t pid = waitpid(child_pid, &status, 0);
if (pid == -1) {
perror("waitpid failed");
}# ./pipex infile cmd1 cmd2 outfile
pipe()
|
|-- fork()
|
|-- child // cmd1
: |--dup2()
: |--close end[0]
: |--execve(cmd1)
:
|-- parent // cmd2
|--dup2()
|--close end[1]
|--execve(cmd2)pipe()creates a communication channel, allowing data to be transferred from one process to another.fork()generates a new child process, so we can run two commands within a single program.- Child Process:
- Uses
dup2()to redirect input/output. - Closes the read end of the pipe.
- Executes
cmd1withexecve().
- Uses
- Parent Process:
- Uses
dup2()to set up input/output redirection. - Closes the write end of the pipe.
- Executes
cmd2withexecve().
- Uses
For the child process:
infilebecomesstdin(input)end[1]becomesstdout(writescmd1output toend[1])
For the parent process:
end[0]becomesstdin(readscmd1output fromend[1])outfilebecomesstdout(writescmd2output tooutfile)
infile outfile
as stdin for cmd1 as stdout for cmd2
| PIPE ↑
| |---------------------------| |
↓ | | |
cmd1 --> end[1] ↔ end[0] --> cmd2
| |
cmd1 |---------------------------| end[0]
output reads end[1]
is written and sends cmd1
to end[1] output to cmd2
(end[1] becomes (end[0] becomes
cmd1 stdout) cmd2 stdin)
end[1]is the child process,end[0]is the parent process.- The child writes, and the parent reads.
- Since for something to be read, it must be written first,
cmd1is executed by the child, andcmd2by the parent. pipexis run like this:./pipex infile cmd1 cmd2 outfile.- FDs
0,1, and2are by default assigned to stdin, stdout, and stderr. infile,outfile, the pipe, stdin, and stdout are all FDs.
ls -la /proc/$$/fd -----------------
0 | stdin |
-----------------
1 | stdout |
-----------------
2 | stderr |
-----------------
3 | infile | // open()
-----------------
4 | outfile | // open()
-----------------
5 | end[0] |
-----------------
6 | end[1] |
-----------------
The PIPEX project was an excellent opportunity to dive deep into how pipes, processes, and input/output redirection work in Unix-like systems. With this project, I replicated the behavior of the shell pipeline operator (|), allowing the output of one command to be connected to the input of another, similar to the following shell command:
What I learned:
🛠️ Manipulating pipes for inter-process communication.
🧠 Using fork() to create child processes and execve() to execute commands.
🔄 Redirecting input and output with dup2().
🔍 Finding commands on the system using environment variables.
This project solidified my understanding of processes and pipes in C, providing me with a strong foundation to work on more complex system-level implementations in C. 💻
The application of execve() also helped me improve command handling and better understand the importance of file structures and process management.
It was an incredible challenge that taught me a lot about how operating systems handle inter-process communication! 🔥
Now, I can apply this knowledge to more advanced projects and gain a better understanding of how operating system programs work day-to-day! 🚀
Foi um desafio incrível que me ensinou muito sobre como os sistemas operacionais lidam com comunicação entre processos! 🔥
Agora, posso aplicar esse conhecimento em projetos mais avançados e entender melhor como programas de sistemas operacionais funcionam no dia a dia! 🚀
Pronto! Você pode copiar isso diretamente para a seção de conclusão do seu GitHub. Se precisar de mais algum ajuste, é só avisar!