Skip to content

noecrn/42sh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

377 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

42SH — POSIX Shell Implementation

Language Standard Build Status

A POSIX-compliant Unix shell written from scratch in C, implementing the Shell Command Language (SCL) as specified by the XCU volume of the POSIX standard.

🐚 Key Highlight: 42sh replicates the core behavior of bash --posix — from lexing and parsing shell syntax into an AST, to executing pipelines, redirections, builtins, functions, and control flow constructs — all without relying on any external shell libraries.


✨ Features

Command Execution

  • Simple Commands: Binary execution via execvp with PATH resolution.
  • Command Lists: Sequential execution separated by ; or newlines.
  • Pipelines: cmd1 | cmd2 | cmd3 with correct exit status propagation.
  • Negation: ! reverses the exit status of any pipeline.
  • Operators: && and || for conditional chaining.
  • Subshells: ( compound_list ) runs commands in a forked child process.
  • Command Blocks: { compound_list } groups commands without forking.

Control Flow

  • if / elif / else / fi: Full conditional branching.
  • while / until: Condition-based loops.
  • for: Iteration over word lists.
  • case: Pattern matching with |-separated alternatives and ;; terminators.

Quoting & Expansion

  • Single Quotes: Literal string quoting — no expansion inside.
  • Double Quotes: Partial quoting with variable and command substitution.
  • Escape Character: \ to escape special characters.
  • Variables: Assignment, $name, ${name}, and special variables ($?, $$, $@, $*, $#, $1$n, $RANDOM, $UID, $PWD, $OLDPWD, $IFS).
  • Command Substitution: `cmd` and $(cmd).
  • Tilde Expansion: ~ and ~user resolution.
  • Path Expansion (Globbing): *, ?, [...] with full character class support.
  • Arithmetic Expansion: $(( expr )) supporting +, -, *, /, **, &, |, ^, &&, ||, !, ~.
  • Field Splitting: $IFS-based word splitting after expansion.

Redirections

  • Standard Redirections: >, <, >>, >|, <> with optional IONUMBER.
  • File Descriptor Duplication: >&, <&.
  • Here-Documents: << and <<- with expansion support.

Built-in Commands

Builtin Description
echo Print arguments; supports -n, -e, -E
true / false Return exit code 0 or 1
exit Exit the shell with an optional status code
cd Change directory; supports cd -, updates $PWD / $OLDPWD
export Export variables to the environment
unset Unset variables or functions
. (dot) Source a script file in the current shell
continue / break Loop control with optional nesting depth
alias / unalias Define and remove lexer-level aliases
set Shell options: -C (noclobber), -e (errexit), -x (xtrace)

Functions

  • Definition via name() { ... } syntax.
  • Argument passing ($1$n, $@, $#).
  • Redirections on function calls.
  • Nested function definitions supported.

Comments

  • #-prefixed comments, context-aware (not triggered inside words or quotes).

🛠️ Technical Stack

  • Language: C (C99 Standard)
  • Compiler Flags: -std=c99 -pedantic -Werror -Wall -Wextra -Wvla
  • Build System: GNU Autotools (autoconf, automake, optionally libtool)
  • Parsing Strategy: Hand-written recursive descent parser
  • IR: Abstract Syntax Tree (AST)
  • Tools: Valgrind (memory leaks), GDB / libtool execute (debugging)

🚀 Installation & Usage

Prerequisites

  • GCC (C99-compatible)
  • GNU Autotools (autoconf, automake)
  • make

Build

autoreconf --install
./configure
make

The binary is produced at src/42sh.

Install

./configure --prefix=/your/install/path
make install
/your/install/path/bin/42sh

Run

# Interactive mode (stdin)
./src/42sh

# Execute a script file
./src/42sh script.sh

# Execute an inline string
./src/42sh -c "echo hello world"

# Read from stdin via pipe or redirect
echo "echo piped" | ./src/42sh
./src/42sh < script.sh

🧪 Testsuite

The testsuite covers both unit and functional tests and is integrated into the build system.

# Run functional tests only
BIN_PATH="$(pwd)/src/42sh" OUTPUT_FILE="$(pwd)/out" make check

# Run with coverage (unit + functional)
BIN_PATH="$(pwd)/src/42sh" COVERAGE="yes" make check

# Validate against bash --posix
BIN_PATH="$(which bash)" OUTPUT_FILE="$(pwd)/out" make check

Tests are organized by category under tests/categories/, one file per test. Each test compares stdout, stderr, and exit status against bash --posix.


📂 Project Structure

├── configure.ac
├── Makefile.am
├── src/
│   ├── main.c
│   ├── Makefile.am
│   ├── ast/               # AST node definitions, construction and cleanup
│   ├── builtins/          # Built-in command implementations (echo, cd, exit, export, unset, dot, break, continue)
│   ├── execution/         # AST execution engine (cmd, pipe, redir, if, loop, block, function, subshell)
│   ├── expansion/         # Variable, quote, command substitution, field splitting
│   ├── io/                # I/O backend abstraction
│   ├── lexer/             # Tokenizer — words, operators, tokens
│   ├── logic/             # &&, ||, negation logic
│   ├── parser/            # Recursive descent parser → AST (if, for, loop, pipe, redir, ...)
│   ├── pretty_print/      # AST pretty-printer for debugging
│   └── variables/         # Variable storage (hash map), conversions
└── tests/
    ├── Makefile.am
    ├── testsuite.sh        # Main testsuite runner
    ├── testsuite_list.sh   # Test category listing
    ├── test_lexer.c
    ├── test_parser.c
    ├── test_ast.c
    ├── test_exec.c
    ...

📚 References


👥 Authors

This project was built by a team of engineering students:

  • Noé Cornu
  • Baptiste Rio
  • Clara Verrier
  • Juliette Tougma
  • Edgar Lugnier

"The answer to this is very simple. It was a joke. It had to be a number, an ordinary, smallish number, and I chose that one." — Douglas Adams on 42

About

POSIX-compliant Shell implementation in C. Handles process execution, pipes, redirections, and memory management.

Topics

Resources

Stars

Watchers

Forks

Contributors