A custom Unix shell implemented from scratch in C++ as part of an Advanced Operating Systems assignment. It replicates core shell behaviors including command parsing, process management, directory navigation, and persistent command history.
AOS_Shell-master/
├── shell.cpp # Main entry point — REPL loop, prompt, signal handling, tokenization
├── ls.cpp # Directory listing with permission details
├── ls.h # Header for ls module
├── echo.cpp # Echo command with quote-aware space trimming
├── echo.h # Header for echo module
├── history.cpp # Command history (in-session + persistent via history.txt)
├── history.h # Header for history module
├── pinfo.cpp # Process info reader via /proc filesystem
├── pinfo.h # Header for pinfo module
├── search.cpp # Recursive DFS file/folder search
├── search.h # Header for search module
├── newprocess.cpp # Foreground & background process execution via fork/exec
├── newprocess.h # Header for newprocess module
└── history.txt # Persistent history file (auto-managed)
┌─────────────────────────────────────────────────────────┐
│ main() │
│ 1. Set logicalRootDir = current working directory │
│ 2. Register SIGINT signal handler │
│ 3. Load history from history.txt into vector │
│ 4. Enter REPL (Read–Eval–Print Loop) │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ printPath() │
│ Displays: username@systemname:~<relative_path>> │
│ Uses: getlogin_r(), uname(), getcwd() │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ readCommand() │
│ Enables raw terminal mode (termios) │
│ Reads char-by-char: │
│ ↑ Arrow → navigate back in history │
│ ↓ Arrow → navigate forward in history │
│ Backspace → delete last character │
│ Enter → submit command │
│ Disables raw mode after input │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ tokenizationInput() on ";" │
│ Splits compound commands: "cmd1 ; cmd2 ; cmd3" │
│ Each sub-command is processed independently │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ tokenizationInput() on " " │
│ Splits each sub-command into tokens by space │
│ token[0] = command name │
│ token[1..n] = arguments │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ performOperation() │
│ Routes token[0] to correct handler: │
│ ┌──────┬──────────────────────────────────────────┐ │
│ │ cd │ handle_cd_operation() │ │
│ │ pwd │ handle_pwd_operation() │ │
│ │ echo │ handle_echo_operation() │ │
│ │ ls │ handle_ls_command() │ │
│ │search│ handle_search_command() │ │
│ │histor│ handle_history_command() │ │
│ │pinfo │ handle_pinfo_command() │ │
│ │ else │ handle_new_process() [fork + exec] │ │
│ └──────┴──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ On "exit": reflectInhistoryFile() │
│ Writes all session commands back to history.txt │
│ (Maintains max 20 commands — oldest dropped first) │
└─────────────────────────────────────────────────────────┘
| Component | Responsibility |
|---|---|
logicalRootDir |
Global char array storing the shell's home/root directory (set to cwd at launch) |
priviousDirectory |
Tracks last visited directory for cd - |
background_processes |
Vector of background process PIDs |
printPath() |
Builds and prints user@system:~relpath> prompt |
enableRawMode() / disableRawMode() |
Toggles POSIX termios raw mode for arrow key detection |
readCommand() |
Character-by-character input handler with history navigation |
tokenizationInput() |
Splits input by a given delimiter (; then space) |
performOperation() |
Central dispatcher — routes to all sub-command handlers |
sigint_handler() |
Catches Ctrl+C, forwards SIGINT to foreground child process only |
Implements Unix ls with flag support using opendir(), readdir(), and stat().
| Function | Description |
|---|---|
getOwnerPermission() |
Extracts user rwx bits from st_mode |
getGroupPermission() |
Extracts group rwx bits from st_mode |
getOtherPermission() |
Extracts others rwx bits from st_mode |
getPermissionsforfile() |
Combines all permissions into drwxrwxrwx string |
getpermission() |
Populates owner (via getpwuid), group (via getgrgid), and modification time |
printLongFormat() |
Prints a single file/dir entry in long format |
printDetailsForfile() |
Handles the case where the ls target is a file, not a directory |
printDetails() |
Iterates directory entries, filters hidden files, delegates to print functions |
handle_ls_command() |
Entry point — parses flags, resolves paths, dispatches to printDetails() |
| Function | Description |
|---|---|
trimLeadingSpace() |
Skips leading spaces before parsing begins |
handle_echo_operation() |
Parses the input string character-by-character. Preserves spaces inside "...", collapses multiple spaces outside quotes, and errors on unclosed quotes |
| Function | Description |
|---|---|
initializeVector() |
Reads history.txt line-by-line into previousCommands vector at shell startup |
updateVector() |
Appends new command to the in-memory vector. Drops the oldest command if size ≥ 20 |
handle_history_command() |
Prints last N commands from vector (max 20). Prints all if no argument |
reflectInhistoryFile() |
Writes all in-memory commands back to history.txt on shell exit (via open() + write()) |
Reads process data directly from the Linux /proc virtual filesystem.
| Function | Description |
|---|---|
getStatus() |
Reads State: field from /proc/<pid>/status. Maps to R/S/Z/T. Appends + if process is in foreground |
getMemory() |
Reads VmSize: field from /proc/<pid>/status |
getExecutablePath() |
Reads the symlink at /proc/<pid>/exe using readlink() to get full executable path |
getInformation() |
Orchestrates reading status file and calling all getter functions |
printDetails() |
Prints PID, status, memory, and executable path |
handle_pinfo_command() |
If no arg → uses getpid() (shell itself). If PID given → fetches that PID's info |
| Function | Description |
|---|---|
search_recursive() |
Recursively traverses directory tree using opendir()/readdir(). Skips . and ... Returns true as soon as a match is found (early exit) |
handle_search_command() |
Validates argument (filename only, no slashes). Gets current dir via getcwd(). Calls search_recursive() and prints True / False |
Implements foreground and background process execution using POSIX fork() and execvp().
| Function | Description |
|---|---|
prepareCommandArgs() |
Converts vector<string> to vector<char*> (null-terminated) for use with execvp |
createProcess() |
Calls execvp() to replace child process image with the target program |
execute_foreground_process() |
Parent waits via waitpid(). Child calls execvp |
execute_background_process() |
Parent returns immediately, logs background PID. Child calls setpgid(0,0) (new process group) then execvp |
handle_new_process() |
Detects & at end of tokens, forks, and routes to foreground or background execution |
Changes the current working directory. Tracks previous directory for cd -.
| Usage | Description |
|---|---|
cd |
Navigate to logical root (shell's startup directory) |
cd ~ |
Navigate to logical root directory |
cd .. |
Navigate to parent directory |
cd . |
Stay in current directory (no-op, updates priviousDirectory) |
cd - |
Toggle back to the previously visited directory |
cd <path> |
Navigate to an absolute or relative path |
Note: More than one argument results in an
"Invalid arguments"error.
Prints the absolute path of the current directory.
pwd
# Output: /home/user/Desktop/myprojectUses the POSIX getcwd() system call internally.
Prints text to standard output with intelligent whitespace handling.
| Usage | Description |
|---|---|
echo Hello World |
Collapses multiple spaces → prints Hello World |
echo "Hello World" |
Preserves spaces inside quotes → prints Hello World |
echo "Hello" World |
Mixed mode — quoted sections preserved, outside collapsed |
echo |
Prints an empty newline |
Error case: Unclosed double quotes → prints
"Please close the double quotes".
Lists files and directories using opendir() and stat().
| Usage | Description |
|---|---|
ls |
List non-hidden files in current directory |
ls -a |
List all files including hidden (. prefix) |
ls -l |
Long format — shows permissions, links, owner, group, size, date, name |
ls -la or ls -al |
All files in long format |
ls . |
List current directory |
ls .. |
List parent directory |
ls ~ |
List logical root directory |
ls <path> |
List a specific file or directory |
ls -l <path> |
Long format listing of a specific path |
Long format output columns:
-rwxr-xr-x 2 user group 4096 May 23 12:00 filename
│ │ │ │ │ │ └─ name
│ │ │ │ │ └─ modification time
│ │ │ │ └─ size in bytes
│ │ │ └─ group name
│ │ └─ owner name
│ └─ hard link count
└─ permissions (type + owner/group/others rwx)
Searches for a file or folder name recursively under the current directory using Depth-First Search (DFS).
| Usage | Description |
|---|---|
search <filename> |
Search for <filename> under current directory |
search myfile.txt
# Output: True (if found)
# Output: False (if not found)Constraints: Accepts only a filename, not a path. Slash
/characters in the argument are rejected.
Displays previously executed commands. History is persistent across sessions (stored in history.txt) and limited to 20 commands maximum.
| Usage | Description |
|---|---|
history |
Print all stored commands |
history <N> |
Print the last N commands (N must be ≤ 20) |
history 5
# Prints the last 5 commands enteredSession behavior: Initialized from
history.txton startup. Written back onexit. Arrow keys (↑ ↓) also let you navigate history inline.
Reads process information from the Linux /proc virtual filesystem.
| Usage | Description |
|---|---|
pinfo |
Display info about the current shell process |
pinfo <PID> |
Display info about any process with the given PID |
Output fields:
Pid -- 12345
Process Status -- S+
Memory -- 45678 kB
Executable Path -- /usr/bin/bash
| Field | Description |
|---|---|
Pid |
Process ID |
Process Status |
R (Running), S (Sleeping), Z (Zombie), T (Stopped). + appended if foreground |
Memory |
Virtual memory size (VmSize) from /proc/<pid>/status |
Executable Path |
Resolved symlink from /proc/<pid>/exe |
Any unrecognized command is passed to the system via fork() + execvp().
| Usage | Description |
|---|---|
<command> [args] |
Run command in foreground — shell waits for it to complete |
<command> [args] & |
Run command in background — shell returns immediately, prints PID |
sleep 10 # Foreground: shell waits 10 seconds
sleep 10 & # Background: immediately returns, prints PIDForeground: Parent process calls waitpid() and blocks until child exits.
Background: Child calls setpgid(0, 0) to detach into its own process group, parent continues immediately.
exitGracefully exits the shell. Before exiting, writes the full in-memory command history back to history.txt.
Multiple commands can be chained on one line using ;.
cd Desktop ; ls -l ; pwdEach command is executed sequentially in order.
| Signal | Trigger | Behavior |
|---|---|---|
SIGINT |
Ctrl+C |
Forwards SIGINT to the current foreground child process only; does not kill the shell |
This project is designed to run on Linux / Unix-based systems only (uses POSIX APIs, /proc filesystem, fork, execvp, termios, etc.).
| Requirement | Details |
|---|---|
| OS | Linux (Ubuntu, Fedora, Arch, etc.) or WSL2 on Windows |
| Compiler | g++ (GCC C++ compiler, version ≥ 7) |
| Standard | C++11 or later |
⚠️ Windows Note: Native Windows is not supported. Use WSL2 (Windows Subsystem for Linux) to run this project.
cd path/to/AOS_Shell-masterCompile shell.cpp (it #includes all .cpp files directly):
g++ -o shell shell.cppThis produces an executable file named
shellin the current directory.
./shellYou should see a prompt like:
username@Linux:~>
username@Linux:~> pwd
/home/username/AOS_Shell-master
username@Linux:~> ls -l
...
username@Linux:~> echo "Hello World"
Hello World
username@Linux:~> history 5
...
username@Linux:~> pinfo
...
username@Linux:~> search shell.cpp
True
username@Linux:~> sleep 5 &
Background process PID: 12345
username@Linux:~> exit| Issue | Solution |
|---|---|
g++: command not found |
Install with sudo apt install g++ (Debian/Ubuntu) |
./shell: Permission denied |
Run chmod +x shell then retry |
pinfo shows Unknown for path |
Ensure you're running on Linux with /proc available |
history.txt not found |
The shell auto-creates it on first run. If it errors, create it manually: touch history.txt |
| Arrow keys not working | Ensure terminal supports ANSI escape sequences (standard Linux terminals do) |
| Technology | Usage |
|---|---|
| C++ | Core implementation language |
| POSIX API | fork, execvp, waitpid, getcwd, chdir, stat, opendir, readdir |
| termios | Raw terminal mode for real-time arrow key and backspace input |
Linux /proc FS |
Process status, memory, and executable path in pinfo |
| uname / getlogin_r | System and username retrieval for the prompt |
| SIGINT / signal() | Signal handling for Ctrl+C forwarding |
| File I/O | fopen/fgets and open/write for persistent history |
Built as part of the Advanced Operating Systems (AOS) Assignment — demonstrating low-level Unix systems programming in C++.