Skip to content

anushkaps/posixcraft-shell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

POSIX Unix Shell in C

A fully functional Unix shell built from scratch in C, implementing process management, job control, pipelines, signal handling, and persistent command history.

TL;DR — Why This Project Matters

  • Built a POSIX-compliant Unix shell from scratch in C
  • Full foreground & background job control (fg, bg, Ctrl-C, Ctrl-Z)
  • Supports pipes, I/O redirection, background execution, and sequencing
  • Uses process groups and terminal control (setpgid, tcsetpgrp)
  • Signal-safe design using sigaction
  • Persistent command history across sessions
  • Compiled with strict warnings (-Wall -Wextra -Werror)

Origin

This project was designed and implemented entirely by me as part of the
CS3 – Operating Systems & Networks (Monsoon 2025) course at IIIT Hyderabad.

This repository is a cleaned, documented, standalone public version of the work.

How to Build and Run

Prerequisites

  • GCC compiler
  • Make
  • Unix-like operating system (Linux, macOS, WSL)

Build

make clean && make

Output: The compiled binary is shell.out

Run

./shell.out

Or build and run in one command:

make run

Exit

Press Ctrl-D (EOF). The shell will print "logout" and exit.

Example

# Build the shell
make clean && make

# Run the shell
./shell.out

# You'll see a prompt like:
<username@hostname:~/path>

# Try a simple command
<user@host:~> echo "Hello, World!"
Hello, World!

# Exit with Ctrl-D
<user@host:~> [Ctrl-D]
logout

Commands Reference

External Commands

The shell can execute any external command available in your system PATH:

<user@host:~> ls -la
<user@host:~> cat file.txt
<user@host:~> grep pattern file.txt
<user@host:~> sleep 5

Error handling: If a command doesn't exist, the shell prints Command not found!

Built-in Commands

hop - Change Directory

Syntax: hop [~ | . | .. | - | path]

Arguments:

  • ~ or no arguments: Change to home directory
  • .: Stay in current directory (no-op)
  • ..: Move to parent directory
  • -: Move to previous directory (if available)
  • path: Change to specified relative or absolute path

Examples:

<user@host:~/project> hop ~
<user@host:~> hop ..
<user@host:/home> hop user/project
<user@host:~/project> hop -
<user@host:/home> hop user/project .. -
<user@host:~/project>

Error: Prints No such directory! if directory doesn't exist.

reveal - List Directory Contents

Syntax: reveal [-a] [-l] [~ | . | .. | - | path]

Flags:

  • -a: Show all files including hidden ones (files starting with .)
  • -l: Display one entry per line
  • Flags can be combined: -la, -al, -aal, etc.

Arguments: Same as hop command (directory navigation)

Examples:

# List current directory
<user@host:~> reveal
file1.txt file2.txt directory1

# List with hidden files
<user@host:~> reveal -a
.git .gitignore file1.txt file2.txt directory1

# List one per line
<user@host:~> reveal -l
file1.txt
file2.txt
directory1

# List hidden files one per line
<user@host:~> reveal -la
.git
.gitignore
file1.txt
file2.txt
directory1

# List specific directory
<user@host:~> reveal ~/projects
project1 project2

# List previous directory
<user@host:~> reveal -

Features:

  • Files are sorted lexicographically (ASCII order)
  • Default format: space-separated (like ls)
  • Line format: one entry per line (when -l flag is used)

Errors:

  • No such directory! - if directory doesn't exist
  • reveal: Invalid Syntax! - if too many path arguments provided

log - Command History

Syntax: log [purge | execute <index>]

Subcommands:

  • No arguments: Print all stored commands (oldest to newest)
  • purge: Clear all history
  • execute <index>: Execute command at index (1-indexed, newest to oldest)

Examples:

# View history
<user@host:~> log
reveal ~
hop ..
reveal
hop ~

# Execute most recent command (index 1)
<user@host:~> log execute 1

# Execute second most recent command (index 2)
<user@host:~> log execute 2

# Clear history
<user@host:~> log purge
<user@host:~> log

Features:

  • Stores up to 15 commands
  • History persists across shell sessions (saved to ~/.cshell_history)
  • Doesn't store duplicate consecutive commands
  • Doesn't store commands containing log as atomic command name
  • Stores entire command as entered

Error: log: Invalid Syntax! for incorrect syntax.

activities - List Active Jobs

Syntax: activities

Output Format: [pid] : command_name - State

Examples:

<user@host:~> sleep 100 &
[1] 12345
<user@host:~> cat file.txt &
[2] 12346
<user@host:~> activities
[12346] : cat - Running
[12345] : sleep - Running

# After stopping a job
<user@host:~> fg 1
sleep 100
^Z
[1] Stopped sleep
<user@host:~> activities
[12345] : sleep - Stopped
[12346] : cat - Running

Features:

  • Output sorted lexicographically by command name
  • Shows PID, command name, and state (Running/Stopped)
  • Automatically removes terminated processes from list

ping - Send Signal to Process

Syntax: ping <pid> <signal_number>

Examples:

# Send SIGTERM (15) to process 12345
<user@host:~> ping 12345 15
Sent signal 15 to process with pid 12345

# Signal number is taken modulo 32
<user@host:~> ping 12345 47
Sent signal 47 to process with pid 12345
# (Actually sends signal 15, since 47 % 32 = 15)

Features:

  • Signal number is taken modulo 32 before sending
  • Validates process existence before signaling

Errors:

  • No such process found - if process doesn't exist
  • Invalid syntax! - if signal number is not valid

fg - Bring Job to Foreground

Syntax: fg [job_number]

Examples:

# Start a background job
<user@host:~> sleep 100 &
[1] 12345

# Bring it to foreground
<user@host:~> fg 1
sleep 100
# Job is now running in foreground

# Bring most recent job to foreground (no argument)
<user@host:~> fg
sleep 100

Features:

  • If job is stopped, sends SIGCONT to resume it
  • Shell waits for job to complete or stop again
  • Prints the entire command when bringing to foreground
  • Uses most recently created job if no job number provided

Error: No such job if job number doesn't exist.

bg - Resume Job in Background

Syntax: bg [job_number]

Examples:

# Start and stop a job
<user@host:~> sleep 100 &
[1] 12345
<user@host:~> fg 1
sleep 100
^Z
[1] Stopped sleep

# Resume in background
<user@host:~> bg 1
[1] sleep &

# Resume most recent job (no argument)
<user@host:~> bg
[1] sleep &

Features:

  • Sends SIGCONT to resume stopped job
  • Job continues running in background
  • Prints [job_number] command_name & when resuming

Errors:

  • Job already running - if job is already running
  • No such job - if job number doesn't exist

Command Operators

Pipes (|)

Syntax: command1 | command2 | ... | commandN

Examples:

# Count lines in file
<user@host:~> cat file.txt | wc -l

# Search and count
<user@host:~> cat file.txt | grep "pattern" | wc -l

# Multiple pipes
<user@host:~> ls -la | grep ".txt" | sort | head -5

Input Redirection (<)

Syntax: command < filename

Examples:

# Read file contents
<user@host:~> cat < input.txt

# Sort file contents
<user@host:~> sort < data.txt

Error: No such file or directory if file doesn't exist.

Output Redirection (> and >>)

Syntax: command > filename or command >> filename

Examples:

# Overwrite file
<user@host:~> echo "Hello" > output.txt

# Append to file
<user@host:~> echo "World" >> output.txt

# Redirect command output
<user@host:~> ls -la > file_list.txt

Features:

  • >: Creates/overwrites file
  • >>: Appends to file
  • Multiple output redirections: only last one takes effect

Error: Unable to create file for writing if file cannot be created.

Combined Redirection

Examples:

# Input and output redirection
<user@host:~> cat < input.txt > output.txt

# Pipes with redirection
<user@host:~> cat < input.txt | grep "pattern" > output.txt

# Multiple redirections (last one wins)
<user@host:~> cat < input1.txt < input2.txt > output.txt
# Only input2.txt is used

Sequential Execution (;)

Syntax: command1 ; command2 ; ... ; commandN

Examples:

# Execute multiple commands
<user@host:~> echo "First" ; echo "Second" ; echo "Third"
First
Second
Third

# Mix with other operators
<user@host:~> ls ; pwd ; whoami

Features:

  • Executes commands in order
  • Waits for each command to complete before starting next
  • Continues execution even if a command fails
  • Prompt displayed only after all commands finish

Background Execution (&)

Syntax: command &

Examples:

# Run command in background
<user@host:~> sleep 100 &
[1] 12345
<user@host:~> # Shell immediately shows prompt

# Background with pipes
<user@host:~> cat file.txt | grep "pattern" | wc -l &
[2] 12346

# Background with redirection
<user@host:~> cat < input.txt > output.txt &
[3] 12347

Features:

  • Forks child process but doesn't wait for completion
  • Prints [job_number] process_id when job starts
  • Immediately displays new prompt
  • Checks for completed background processes before parsing input
  • Prints completion messages:
    • command_name with pid process_id exited normally
    • command_name with pid process_id exited abnormally
  • Background processes don't have terminal access for input

Keyboard Shortcuts

Ctrl-C (SIGINT)

Interrupts the current foreground process.

Behavior:

  • Sends SIGINT to foreground process group
  • Shell remains responsive (doesn't terminate)
  • Process is terminated

Example:

<user@host:~> sleep 100
^C
<user@host:~> # Shell continues

Ctrl-Z (SIGTSTP)

Stops the current foreground process and moves it to background.

Behavior:

  • Sends SIGTSTP to foreground process group
  • Process is stopped and moved to background job list
  • Prints [job_number] Stopped command_name
  • Shell continues accepting commands

Example:

<user@host:~> sleep 100
^Z
[1] Stopped sleep
<user@host:~> # Shell continues, job is stopped
<user@host:~> bg 1
[1] sleep &

Ctrl-D (EOF)

Exits the shell.

Behavior:

  • Detects EOF condition
  • Terminates all remaining child processes
  • Prints "logout"
  • Exits with status 0

Example:

<user@host:~> [Ctrl-D]
logout

Internals

Detailed architecture and implementation notes are available in
docs/INTERNALS.md.

Known Limitations

  • No shell scripting (loops, conditionals)
  • No globbing or environment variable expansion
  • Designed for learning OS internals, not production use

Compilation & Code Quality

  • Compiler: GCC with C99 standard
  • POSIX Compliance: POSIX.1-2008
  • Flags: -Wall -Wextra -Werror (strict warnings)
  • Tested on: Linux (Ubuntu 22.04), WSL2
  • Modular design with clear separation of concerns
  • Zero memory leaks with proper resource cleanup
  • Robust error handling and signal-safe design

About

A Unix-like shell in C supporting pipelines, I/O redirection, background execution, and job control using POSIX system calls.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors