A custom Unix shell implemented in C from scratch, supporting both interactive and non-interactive execution modes. Built to replicate core Bash shell behaviour using low-level POSIX system calls.
- Interactive & Non-Interactive modes — detects whether input is coming from a terminal or a script/pipe and behaves accordingly via
isatty() - External command execution — runs any system command (
ls,cat,gcc, etc.) by forking a child process and usingexecvp() - Built-in commands — implements
cd,env,help, andexitnatively without spawning a child process - Process management — uses
fork()+waitpid()withWUNTRACEDto correctly wait for child processes and handle termination via exit or signal - Error handling — graceful error reporting for invalid commands, failed forks, failed
execvp(), and missingcdarguments
- Piping (
|) is not supported - Output redirection (
>) is not supported - These are planned for a future implementation
miniShellInC/
├── main.c # Entry point — detects interactive vs non-interactive mode
├── shell.h # Header — all prototypes, macros, and includes
├── shell_interactive.c # REPL loop — prompt, read, parse, execute, repeat
├── shell_no_interactive.c # Script mode — reads stdin line by line until EOF
├── execute_args.c # Dispatcher — routes to built-ins or new_process()
├── new_process.c # Process creation — fork(), execvp(), waitpid()
├── own_cd.c # Built-in cd — wraps chdir() with error handling
├── own_exit.c # Built-in exit — exits shell with optional status code
├── own_env.c # Built-in env — prints all environment variables
└── own_help.c # Built-in help — prints usage information
Execution flow:
User Input → read_line() → split_line() → execute_args() → built-in OR new_process()
Requirements: GCC, Linux/Unix environment (or WSL on Windows)
# Clone the repository
git clone https://github.com/crackedmob/miniShellInC.git
cd miniShellInC
# Compile
gcc -Wall -Wextra -Werror *.c -o myshell
# Run interactively
./myshell
# Run non-interactively (script mode)
echo "ls" | ./myshell# Navigate directories
$ cd /home/user/documents
$ cd ..
# Run external commands
$ ls -la
$ cat file.txt
$ gcc main.c -o program
# View environment variables
$ env
# Get help
$ help
# Exit the shell
$ exit
$ exit 1- Built-in commands are dispatched via two parallel arrays — one of command name strings, one of function pointers — keeping the dispatcher clean and easily extensible
cdmust be a built-in because a child process changing its directory would not affect the parent shell's working directorywaitpid()withWUNTRACEDensures the parent shell correctly waits for child processes that exit normally or are killed by a signalexecvp()is used overexecve()to leveragePATHresolution automaticallygetline()is used overfgets()for dynamic buffer allocation, handling input of any length safely- Tokenization uses
strtok()withTOK_DELIMto handle spaces, tabs, and newlines
- Low-level process lifecycle management using
fork(),exec(), andwaitpid() - Why certain commands like
cdmust be built into the shell at the OS level - How shells distinguish between interactive and non-interactive input via
isatty() - Function pointer arrays as a clean dispatch pattern in C
- Dynamic memory management with
getline()andrealloc()