[42 Cursus] A C program that mimics the behavior of shell pipes (|), allowing you to execute commands in a pipeline with input and output redirection.
This project was designed to enhance skills in process management, file handling, and C programming.
Keywords
- Pipes
- Process Management
- File Descriptors
- Error Handling
- Input/Output Redirection
- Shell Commands
- Forking
- System Calls
- Overview
- Features
- Bonus Features
- Functions Allowed
- How It Works
- What I Learned
- Installation
- Author
- Acknowledgments
The pipex project is a C program that simulates the behavior of shell pipes (|). It takes four arguments: two file names and two shell commands. The program executes the commands in a pipeline, redirecting the input and output as specified. This project was created to deepen understanding of Unix process management, file handling, and error handling in C.
- Pipeline Execution: Execute shell commands in a pipeline, similar to the
|operator in Unix shells. - Input/Output Redirection: Redirect input from a file and output to another file using file descriptors.
- Error Handling: Gracefully handle errors, ensuring no unexpected crashes or memory leaks.
- Basic Command Support: Support for shell commands with arguments.
- Memory Management: clean and efficient.
- Multiple Pipes: Handle more than two commands in a pipeline (e.g.,
cmd1 | cmd2 | cmd3). - Here Document: Support for
<<and>>redirection when the first parameter ishere_doc. - Advanced Error Handling: Improved error messages and handling for edge cases.
The following functions are authorized for use in the pipex project. Each function is briefly described to explain its purpose and relevance to the project.
open(): Opens a file and returns a file descriptor. Used to read from or write to files.close(): Closes a file descriptor, freeing up resources.unlink(): Deletes a file from the filesystem. Useful for cleaning up temporary files.
fork(): Creates a new process by duplicating the calling process. Used to execute commands in child processes.execve(): Replaces the current process image with a new one. Used to execute shell commands.wait(): Waits for a child process to terminate. Useful for synchronizing parent and child processes.waitpid(): Waits for a specific child process to terminate. Provides more control thanwait().
pipe(): Creates a pipe, which is a unidirectional data channel for inter-process communication. Used to connect commands in a pipeline.dup(): Duplicates a file descriptor. Useful for redirecting input/output.dup2(): Duplicates a file descriptor and assigns it to a specified file descriptor number. Used to redirect input/output to/from pipes.
malloc(): Allocates memory on the heap. Used for dynamic memory allocation.free(): Deallocates memory previously allocated bymalloc(). Prevents memory leaks.
perror(): Prints an error message tostderrbased on the current value oferrno. Useful for debugging.strerror(): Returns a string describing the error code stored inerrno. Useful for generating custom error messages.
read(): Reads data from a file descriptor into a buffer. Used to read input from files or pipes.write(): Writes data from a buffer to a file descriptor. Used to write output to files or pipes.
exit(): Terminates the program with a specified status code. Used to handle errors or normal termination.access(): Checks if a file exists or if the program has permission to access it. Useful for error handling.
The program takes four arguments: two file names and two shell commands. It executes the commands in a pipeline, redirecting the input and output as specified.
./pipex infile "ls -l" "wc -l" outfileThis behaves the same as the following shell command:
< infile ls -l | wc -l > outfiletime: Usetimeto measure the execution time of commands, especially when usingsleep. This helps verify if commands are executed concurrently.env: Useenvto check if the environment variables (e.g.,PATH) are correctly passed to the commands.env -i: Useenv -ito test how your program handles commands without environment variables.echo $?: Useecho $?to capture and verify the exit status of commands.which ls: Usewhichcommand to search the path of the commands, and to use absolute commands in our pipex.cp /usr/bin/ls .: use this command to copy an original command to our current directory, to test relative paths in our pipex.unset PATHorexport PATH="": use one of these commands to test our pipex behaviour without PATHvalgrind --trace-children=yes --track-fds=yes: Use these flags to track if your are managing the children processes and fds correctly
| Input Command | Shell Equivalent | Description | Expected Output |
|---|---|---|---|
./pipex empty "ls -l" "wc -l" outfile |
ls -l | wc -l > outfile |
Lists files in the current directory and counts lines (no input file needed). | Number of lines from ls -l written to outfile. |
./pipex empty "echo Hello" "wc -c" outfile |
echo Hello | wc -c > outfile |
Echoes "Hello" and counts characters (no input file needed). | Number of characters in "Hello" written to outfile. |
./pipex empty "whoami" "tr 'a-z' 'A-Z'" outfile |
whoami | tr 'a-z' 'A-Z' > outfile |
Outputs the current username and converts it to uppercase. | Uppercase username written to outfile. |
./pipex empty "seq 10" "tail -n 5" outfile |
seq 10 | tail -n 5 > outfile |
Generates numbers from 1 to 10 and extracts the last 5 numbers. | Numbers 6 to 10 written to outfile. |
./pipex empty "env" "grep PATH" outfile |
env | grep PATH > outfile |
Lists all environment variables and filters lines containing "PATH". | Lines containing "PATH" written to outfile. |
./pipex infile "cat -e" "cat -e" outfile |
< infile cat -e | cat -e > outfile |
Outputs the content of infile with -e flag applied twice. |
Content of infile with -e flag applied twice written to outfile. |
./pipex infile "ls -la" "cat -e" outfile |
< infile ls -la | cat -e > outfile |
Lists files in the current directory and applies cat -e. |
Output of ls -la with cat -e applied written to outfile. |
./pipex infile "sleep 3" "ls" outfile |
< infile sleep 3 | ls > outfile |
Waits for 3 seconds and lists files in the current directory. | Output of ls written to outfile. |
./pipex infile "grep ep" "wc -w" outfile |
< infile grep ep | wc -w > outfile |
Searches for lines containing "ep" in infile and counts words. |
Number of words in lines containing "a1" written to outfile. |
./pipex infile "sort" "uniq" outfile |
< infile sort | uniq > outfile |
Sorts the content of infile and removes duplicate lines. |
Sorted and deduplicated content written to outfile. |
./pipex infile "head -n 5" "tail -n 1" outfile |
< infile head -n 5 | tail -n 1 > outfile |
Extracts the first 5 lines of infile and then the last line of those 5. |
The 5th line of infile written to outfile. |
./pipex infile "tr ' ' '\n'" "sort" outfile |
< infile tr ' ' '\n' | sort > outfile |
Splits words in infile by spaces and sorts them. |
Sorted list of words written to outfile. |
./pipex infile "grep pattern" "wc -l" outfile |
< infile grep pattern | wc -l > outfile |
Searches for "pattern" in infile and counts matching lines. |
Number of lines containing "pattern" written to outfile. |
| Input Command | Shell Equivalent | Description | Expected Output |
|---|---|---|---|
./pipex non_existent_file "ls -l" "wc -l" outfile |
< non_existent_file ls -l | wc -l > outfile |
Tries to read from a non-existent file. | non_existent_file: No such file or directory |
./pipex infile "invalid_cmd" "wc -l" outfile |
< infile invalid_cmd | wc -l > outfile |
Tries to execute an invalid command. | invalid_cmd: command not found |
./pipex infile "ls -l" "wc -l" unwritable_file |
< infile ls -l | wc -l > unwritable_file |
Tries to write to an unwritable file. | unwritable_file: Permission denied |
./pipex infile_nopermissions "cat -e" "cat -e" outfile |
< infile_nopermissions cat -e | cat -e > outfile |
Tries to read from a file without permissions. | infile_nopermissions: Permission denied |
./pipex infile "cat -e" "cat -e" outfile_nopermissions |
< infile cat -e | cat -e > outfile_nopermissions |
Tries to write to a file without permissions. | outfile_nopermissions: Permission denied |
./pipex infile "nonexistingcommand" "cat -e" outfile |
< infile nonexistingcommand | cat -e > outfile |
Tries to execute a non-existing command. | nonexistingcommand: command not found |
./pipex infile "cat -e" "nonexistingcommand" outfile |
< infile cat -e | nonexistingcommand > outfile |
Tries to execute a non-existing command as the second command. | nonexistingcommand: command not found |
./pipex infile "cat -e" "cat -nonexistingflag" outfile |
< infile cat -e | cat -nonexistingflag > outfile |
Tries to use a non-existing flag with cat. |
cat: invalid option -- 'o' |
./pipex "" "cat -e" "cat -e" outfile |
< "" cat -e | cat -e > outfile |
Missing input file argument. | : No such file or directory |
| Input Command | Shell Equivalent | Description | Expected Output |
|---|---|---|---|
./pipex infile "cmd1" "cmd2" "cmd3" outfile |
< infile cmd1 | cmd2 | cmd3 > outfile |
Executes three commands in a pipeline. | Output of cmd3 written to outfile. |
./pipex here_doc LIMITER "cmd" "cmd1" outfile |
cmd << LIMITER | cmd1 >> outfile |
Uses here_doc to read input until LIMITER is found, then processes it. |
Output of cmd1 appended to outfile. |
- Process Management: Using
fork(),execve(), andwaitpid()to manage processes. - File Descriptors: Working with
open(),close(),dup(), anddup2()to handle input/output redirection. - Pipes: Creating and managing pipes with
pipe()to connect processes. - Error Handling: Ensuring robust error management to avoid crashes and memory leaks.
- C Programming: Writing clean, efficient, and maintainable C code.
- A C compiler
- Standard libraries.
- Clone this repository:
git clone https://github.com/yourusername/pipex.git cd pipex - Initialize and update the libraries as submodules:
git submodule update --init --recursive
- Compile the project using the provided Makefile:
make
- Run the program with the required arguments:
./pipex infile "cmd1" "cmd2" outfile
For bonus features, use the bonus rule in the Makefile:
make bonus| Name | GitHub Profile | 42 Login |
|---|---|---|
| Oliver King Zamora | OliverKingz | ozamora- |
This project is part of the 42 Cursus, a rigorous programming curriculum that emphasizes hands-on learning and problem-solving. Special thanks to the 42 team for providing this challenging and rewarding project!