A Unix shell implementation in C from scratch - built for learning systems programming
A minimal yet feature-rich Unix shell implementation in C, built from scratch for educational purposes.
cd, pwd, echo, exit, help, jobs, fg, bg, kill, set, export, history< input, > output, >> append|&fg, bg)killmake
./cshell
make clean
make debug
# Basic commands
cshell:~$ pwd
cshell:~$ echo Hello World
# Change directory
cshell:~$ cd /tmp
cshell:/tmp~$ cd -
/home/user
cshell:~$
# I/O Redirection
cshell:~$ echo hello > file.txt
cshell:~$ cat < file.txt
hello
# Pipes
cshell:~$ ls -la | head -5
# Background jobs
cshell:~$ sleep 10 &
[1] 12345
cshell:~$ jobs
[1] Running sleep 10 &
cshell:~$ fg %1
# Environment variables
cshell:~$ set MYVAR hello
cshell:~$ echo $MYVAR
hello
cshell:~$ export MYVAR
cshell/
├── Makefile # Build system
├── include/
│ ├── shell.h # Core types & declarations
│ ├── parser.h # Parser declarations
│ ├── executor.h # Execution engine declarations
│ ├── builtins.h # Built-in command declarations
│ └── jobs.h # Job control declarations
└── src/
├── main.c # Entry point + REPL loop
├── parser.c # Tokenizer + command parsing
├── executor.c # Process creation, execvp, pipes
├── builtins.c # cd, exit, pwd, echo, etc.
└── jobs.c # Background job management
The shell uses a Read-Eval-Print Loop:
| Module | Responsibility |
|---|---|
main.c |
REPL loop, prompt display |
parser.c |
Tokenization, command parsing |
executor.c |
fork(), execvp(), redirections, pipes |
builtins.c |
In-process commands (cd, echo, etc.) |
jobs.c |
Background job tracking, signals |
fork() - Create new processexecvp() - Replace process image with new programpipe() - Create IPC channeldup2() - Set up file descriptor redirectionswaitpid() - Wait for child processchdir() - Change working directoryThis project demonstrates understanding of:
Process Management
fork() vs vfork(), copy-on-write semanticswaitpid()IPC (Inter-Process Communication)
| operatordup2() for redirecting stdin/stdoutSignal Handling
SIGCHLD for child status changesSIGINT (Ctrl+C), SIGTSTP (Ctrl+Z)sigaction()Memory Management
Job Control
pid_t pid = fork();
if (pid == 0) {
// Child process
execvp(cmd, args); // Replace with new program
} else {
// Parent process
waitpid(pid, &status, 0); // Wait for child
}
int fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO); // stdout now writes to file
close(fd);
int pipefd[2];
pipe(pipefd); // pipefd[0] = read, pipefd[1] = write
if (fork() == 0) {
dup2(pipefd[1], STDOUT_FILENO); // Child writes to pipe
close(pipefd[0]);
close(pipefd[1]);
execvp(...);
}
MIT License - feel free to use for learning and education.
This is an educational project. Issues and improvements welcome!