π Simple Shell
A UNIX command line interpreter written in C.
π Project Overview
This project is a minimalist implementation of a UNIX shell. It replicates the core behavior of /bin/sh using low-level system calls and without relying on external libraries beyond the standard C library.
The shell supports:
Interactive and non-interactive mode Execution of commands with arguments PATH resolution Built-in commands (exit, env) Proper error handling Correct exit status propagation Memory management (Valgrind clean) π Learning Objectives
Through this project we practiced:
Process creation (fork) Program execution (execve) Process synchronization (wait) Environment handling (environ) PATH parsing Dynamic memory management System call error handling UNIX process lifecycle π Compilation gcc -Wall -Werror -Wextra -pedantic -std=gnu89 *.c -o hsh π Usage Interactive Mode $ ./hsh $ ls $ ls -l /tmp $ env $ exit Non-Interactive Mode echo "ls -l" | ./hsh βοΈ Features Implemented (Holberton Requirements) β 1. Basic Shell Displays a prompt in interactive mode Reads user input using getline Executes commands using fork and execve Handles EOF (Ctrl + D) β 2. Handle Arguments
Supports commands with arguments:
ls -l /var
Tokenization is implemented using strtok.
β 3. Handle PATH
If the command does not contain / or ., the shell searches for it in the PATH environment variable.
Example:
ls
Internally searches:
/bin/ls /usr/bin/ls ...
If not found β prints error without calling fork.
β 4. fork Must Not Be Called If Command Doesn't Exist
The shell validates command existence before forking:
if (cmd_path == NULL) { fprintf(stderr, "./hsh: %d: %s: not found\n", line_num, argv[0]); return (127); }
This satisfies Holbertonβs strict requirement.
β 5. Built-in: exit
Usage:
exit Exits the shell Returns the last command exit status No arguments required (per project specification) β 6. Built-in: env
Usage:
env
Prints all environment variables using:
extern char **environ; π§ Execution Flow START β Display prompt (if interactive) β Read input (getline) β Remove newline β Tokenize input β Check built-ins (exit / env) β Search in PATH (if needed) β If command not found β print error (NO fork) β fork() β Child β execve() Parent β wait() β Return exit status β Repeat π Main Components πΉ main() Shell loop Input parsing Built-in handling Status tracking πΉ execute_command() Validates executable path Forks process Calls execve Waits for child Returns exit status πΉ find_in_path() Parses PATH Iterates directories Constructs full path Validates using stat Returns full path or NULL πΉ print_env() Iterates through environ Prints environment variables β Error Handling
Handles:
Command not found Fork failure Execve failure Empty input EOF Memory allocation errors
Error format:
./hsh: line_number: command: not found
Exit status 127 is returned when command is not found.
π§΅ Memory Management All dynamically allocated memory is freed cmd_path freed after use line freed before exit No memory leaks (Valgrind clean) π§ͺ Example Valid Command $ ls main.c hsh README.md Invalid Command $ fakecmd ./hsh: 3: fakecmd: not found π System Calls Used fork execve wait write access stat getline malloc free π§Ύ Return Status Behavior Returns the exit status of the last executed command. Returns 127 if command not found. Built-in exit returns last status. π§© Project Constraints (Respected)
β No use of system() β No advanced shell features (pipes, redirections, etc.) β No unnecessary forks β Proper PATH handling β No memory leaks β Only allowed functions used
π¨βπ» Authors AZZAM AL DUYULI && ABDULMALIK BIN AQEEL