Skip to content

bhautik979/Custom_Linux_Shell

Repository files navigation

📁 Custom Linux Shell in C++

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.


📁 Project Structure

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)

🏗️ Detailed Architecture

High-Level Flow

┌─────────────────────────────────────────────────────────┐
│                        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)     │
└─────────────────────────────────────────────────────────┘

Module-Level Architecture

shell.cpp — Core REPL Engine

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

ls.cpp — Directory Listing

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()

echo.cpp — Echo with Quote Awareness

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

history.cpp — Command History Management

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())

pinfo.cpp — Process Information via /proc

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

search.cpp — Recursive File/Folder Search (DFS)

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

newprocess.cpp — Process Creation

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

📖 Detail About All Commands

1. cd — Change Directory

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.


2. pwd — Print Working Directory

Prints the absolute path of the current directory.

pwd
# Output: /home/user/Desktop/myproject

Uses the POSIX getcwd() system call internally.


3. echo — Print Text

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".


4. ls — List Directory Contents

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)

5. search — Recursive File/Folder Search

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.


6. history — Command History

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 entered

Session behavior: Initialized from history.txt on startup. Written back on exit. Arrow keys (↑ ↓) also let you navigate history inline.


7. pinfo — Process Information

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

8. External Commands (Foreground & Background)

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 PID

Foreground: 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.


9. exit — Quit Shell

exit

Gracefully exits the shell. Before exiting, writes the full in-memory command history back to history.txt.


10. Semicolon ; — Command Chaining

Multiple commands can be chained on one line using ;.

cd Desktop ; ls -l ; pwd

Each command is executed sequentially in order.


Signal Handling

Signal Trigger Behavior
SIGINT Ctrl+C Forwards SIGINT to the current foreground child process only; does not kill the shell

🚀 How to Run This Project Locally

Prerequisites

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.


Step-by-Step Setup

Step 1: Navigate to the Project Directory

cd path/to/AOS_Shell-master

Step 2: Compile the Project

Compile shell.cpp (it #includes all .cpp files directly):

g++ -o shell shell.cpp

This produces an executable file named shell in the current directory.

Step 3: Run the Shell

./shell

You should see a prompt like:

username@Linux:~>

Step 4: Try Some Commands

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

Troubleshooting

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 Stack

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++.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages