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.
- Simple Commands: Binary execution via
execvpwithPATHresolution. - Command Lists: Sequential execution separated by
;or newlines. - Pipelines:
cmd1 | cmd2 | cmd3with 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.
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.
- 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~userresolution. - Path Expansion (Globbing):
*,?,[...]with full character class support. - Arithmetic Expansion:
$(( expr ))supporting+,-,*,/,**,&,|,^,&&,||,!,~. - Field Splitting:
$IFS-based word splitting after expansion.
- Standard Redirections:
>,<,>>,>|,<>with optionalIONUMBER. - File Descriptor Duplication:
>&,<&. - Here-Documents:
<<and<<-with expansion support.
| 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) |
- Definition via
name() { ... }syntax. - Argument passing (
$1…$n,$@,$#). - Redirections on function calls.
- Nested function definitions supported.
#-prefixed comments, context-aware (not triggered inside words or quotes).
- Language: C (C99 Standard)
- Compiler Flags:
-std=c99 -pedantic -Werror -Wall -Wextra -Wvla - Build System: GNU Autotools (
autoconf,automake, optionallylibtool) - Parsing Strategy: Hand-written recursive descent parser
- IR: Abstract Syntax Tree (AST)
- Tools: Valgrind (memory leaks), GDB / libtool execute (debugging)
- GCC (C99-compatible)
- GNU Autotools (
autoconf,automake) make
autoreconf --install
./configure
makeThe binary is produced at src/42sh.
./configure --prefix=/your/install/path
make install
/your/install/path/bin/42sh# 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.shThe 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 checkTests are organized by category under tests/categories/, one file per test. Each test compares stdout, stderr, and exit status against bash --posix.
├── 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
...
- POSIX Shell Command Language
- Bash Manual
- Crafting Interpreters
- GNU Autotools Documentation
- shell.multun.net — Shell internals guide
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