Pipex, the magical 42 project where we teach pipes in the shell — because even computers need plumbing lessons! 🚽💻 Get ready to connect commands like a pro and make your shell pipelines flow smoother than a water slide at a summer party! 😄🌊.
Before We Dive Into This Project, Let’s Begin By Familiarizing Ourselves With The Functions That Will Be Of Assistance.
Press enter or click to view image in full size
This Is The Allowed Functions.
These functions — ‘read,’ ‘write,’ ‘open,’ ‘close’, ‘malloc’ and ‘free’ — are widely recognized, even by those not in the developer circle, so there’s no need for extensive explanations.
What About The Access();
#include <unistd.h> int access(const char *pathname, int mode); // return: 0 if OK, −1 on error
All Modes Of access();
The ‘access’ function is a powerful tool in programming that checks whether a specified user can perform specific actions on a file. It evaluates permissions for read, write, and execute operations, providing essential security and control in file handling.
Dup2();
An existing file descriptor is duplicated by either of the following functions:
#include <unistd.h> int dup2(int fd, int fd2); // return: new file descriptor if OK, −1 on error
‘Dup2’ is like making a photocopy of a key to a locked room; you now have two keys that can open the same door, making it handy for managing access to a resource.
Exemple :
#include <unistd.h> #include <fcntl.h>
int main() { int fd = open("output.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
// Check if the file opened successfully if (fd == -1) { exit(1);
// Redirect stdout (file descriptor 1) to the file if (dup2(fd, 1) == -1) {j perror("dup2"); return 1; }
// Now, anything written to stdout will be written to "output.txt" printf("This will be written to 'output.txt'\n");
// Close the file descriptor when done close(fd);
return 0; }
Execve();
execve is like a portal that lets your program transform into another one. It replaces your code with the specified program, transferring control and execution to it, effectively running a different software within your own.
#include <unistd.h> int execve(const char *pathname, char *const argv[], char *const envp[]); // return: −1 on error, no return on succes
Get abkabex’s stories in your inbox
Join Medium for free to get updates from this writer.
// This code will only be executed if execve fails. printf("This line won't be reached because execve replaces the current process.\n");
return 0; }
Fork();
The fork function is like creating a clone of your program; it spawns a new identical process, like a twin, allowing both the original and the new process to run concurrently and independently, each with its own set of resources and memory space.
#include <unistd.h> pid_t fork(void); // Returns: 0 in child, process ID of child in parent, −1 on error
Exemple :
#include <stdio.h> #include <unistd.h>
int main() { pid_t child_pid; // Process ID variable
child_pid = fork(); // Create a new process (child)
if (child_pid == -1) { perror("fork"); return 1; }
if (child_pid == 0) { // This code runs in the child process printf("Hello from the child process!\n"); } else { // This code runs in the parent process printf("Hello from the parent process! Child's PID: %d\n", child_pid); }
return 0; }
pipe();
The pipe function is like a communication channel between two processes, resembling a physical pipe. It allows one process to send data to another, creating a connection for seamless information exchange, much like talking through a secret conduit.
#include <unistd.h> int pipe(int fd[2]); // Returns: 0 if OK, −1 on error
close(pipe_fd[0]); // Close the read end of the pipe in the child } else { // This code runs in the parent process close(pipe_fd[0]); // Close the read end of the pipe in the parent
char message[] = "Hello, Child!"; ssize_t bytes_written = write(pipe_fd[1], message, strlen(message)); if (bytes_written > 0) { printf("Parent sent a message to the child.\n"); }
close(pipe_fd[1]); // Close the write end of the pipe in the parent }
return 0; }
wait();
The wait function is like a guardian at the exit of a funhouse, ensuring that the parent process patiently waits for its child process to complete its performance. It allows for orderly synchronization, making sure the parent doesn't rush ahead until the child is done with its act, ensuring a smooth circus of program execution.
#include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options);
//Both return: process ID if OK, 0 (see later), or −1 on error
if (child_pid == -1) { perror("fork"); return 1; }
if (child_pid == 0) { // This code runs in the child process printf("Child process executing...\n"); sleep(3); // Simulate some work in the child process exit(42); // Exit with a status code } else { // This code runs in the parent process printf("Parent waiting for the child...\n"); wait(&status); // Wait for the child process to complete
printf("Child process has finished.\n");
if (WIFEXITED(status)) { printf("Child exited with status: %d\n", WEXITSTATUS(status)); } }
return 0; }
With the explanations complete, let’s move on to how I implemented my program.