ninxsh is a lightweight shell written in modern C++. It supports:
- Enhanced Terminal Prompt: Modern
username@hostname:path$format with ANSI colors - Smart Prompt Display: Colored prompt for interactive use, plain format when piped
- Builtin Commands (
exit,cd,clear,history,jobs,kill,fg,bg) - External executable support using
fork()andexecvp() - Input/output redirection (
<,>) - Command pipelines (
|) with multiple commands - Background process execution (
&) - Job control and management (
jobs,kill <pid>,fg [job_id],bg [job_id]) - Signal handling (Ctrl+C, Ctrl+Z)
- Path expansion (
~to home directory) - Environment variable expansion (
$HOME,$USER, etc.) - Advanced quote handling (single quotes, double quotes, escape sequences)
- Zombie process cleanup with automatic job status updates
- DoS protection with configurable limits (centralized in
limits.hpp) - Comprehensive test suite for all features
- Command history with persistent storage and execution (
!!,!n)
make # Build the shell in default mode
make debug # Build with debug symbols and without optimizations
make release # Build with optimizations
make sanitize # Build with sanitizers for catching memory issues
make format # Format code using clang-format
make test # Run the test suite
make install # Install the shell (requires sudo)
make clean # Delete build artifacts
make help # Show available commandsFor platform-specific optimizations and features, use the specialized Makefiles:
# macOS (Xcode toolchain, code signing, app bundles)
cd Resources/Mac && make
# Linux (distribution detection, package creation, valgrind)
cd Resources/Linux && make
# Windows (MinGW/MSYS2, installers, portable builds)
cd Resources/Windows && makemacOS (Resources/Mac/):
- Debug symbols for lldb (
make dsym) - Code signing (
make codesign) - App bundle creation (
make bundle) - Apple Silicon support (
make install-arm64)
Linux (Resources/Linux/):
- Auto-detect distribution and package manager
- Memory testing (
make valgrind) - Performance profiling (
make perf) - Package creation (
make deb,make rpm) - Static builds (
make static)
Windows (Resources/Windows/):
- NSIS installer creation (
make installer) - Portable ZIP packages (
make portable) - Static linking for portability
- MSYS2 dependency management (
make deps)
To enable IDE features like auto-complete and jump-to-definition, generate a compile_commands.json:
make clean
bear -- makeninxsh/
├── src/
│ ├── main.cpp # Main loop
│ ├── shell.cpp # Shell class logic
│ ├── command.cpp # Tokenization
│ ├── executor.cpp # External execution logic
│ ├── builtin.cpp # Built-in command handlers
│ ├── utils.cpp # Misc utilities
│ ├── history.cpp # Command history
│ └── jobs.cpp # Job management
├── include/
│ ├── shell.hpp
│ ├── command.hpp
│ ├── executor.hpp
│ ├── builtin.hpp
│ ├── utils.hpp
│ ├── history.hpp
│ ├── jobs.hpp
│ └── limits.hpp # DoS protection constants
├── tests/
│ ├── test_runner.cpp # Test runner framework
│ ├── test_command.cpp # Command parsing tests
│ ├── test_builtin.cpp # Builtin command tests
│ ├── test_utils.cpp # Utility function tests
│ ├── test_executor.cpp # Command execution tests
│ ├── test_io_pipeline.cpp # I/O and pipeline tests
│ ├── test_signal.cpp # Signal handling tests
│ ├── test_history.cpp # Command history tests
│ ├── test_dos_protection.cpp # DoS protection tests
│ └── test_jobs.cpp # Job management tests
├── Resources/
│ ├── Mac/Makefile # macOS-optimized build
│ ├── Linux/Makefile # Linux-optimized build
│ └── Windows/Makefile # Windows-optimized build
├── Makefile # Universal build system
├── README.md # Project documentation
└── .gitignore # Git ignore fileTo generate the initial project structure:
mkdir -p ninxsh/{src,include,tests,Resources/{Mac,Linux,Windows}} \
&& touch ninxsh/src/{main.cpp,shell.cpp,command.cpp,executor.cpp,builtin.cpp,utils.cpp,history.cpp,jobs.cpp} \
&& touch ninxsh/include/{shell.hpp,command.hpp,executor.hpp,builtin.hpp,utils.hpp,history.hpp,jobs.hpp,limits.hpp} \
&& touch ninxsh/tests/{test_runner.cpp,test_command.cpp,test_builtin.cpp,test_utils.cpp,test_executor.cpp,test_io_pipeline.cpp,test_signal.cpp,test_history.cpp,test_dos_protection.cpp,test_jobs.cpp} \
&& touch ninxsh/Resources/{Mac,Linux,Windows}/Makefile \
&& touch ninxsh/{Makefile,README.md,.gitignore}- Project compiles with
make - Print Prompt:
ninxsh > - Reads user input
- Gracefully handles empty input and
Ctrl + C(EOF) - Initial commits in Git
- Run external commands using
fork()andexecvp() - Supports basic Unix commands like
ls,pwd,echo,date, etc. - Basic error handling for invalid commands (shows "command not found")
- Robust input tokenization and conversion to C-style argument arrays
- Handles command execution filure gracefully without crashing
- Proper memory management with
strdup()andfree()
- Implemented
exit - Implemented
cd [path](fallback$HOME) - Implemented
clearusing ANSI escape sequence - Implemented
historywith persistent storage and execution (!!,!n) - Implemented
jobsfor listing background jobs - Implemented
kill <pid>for terminating processes - Implemented
fg [job_id]for bringing jobs to foreground - Implemented
bg [job_id]for resuming jobs in background -
Builtinsare handled in parent process (nofork)
- Handle absolute & relative path commands
- Support for
~expansion to$HOME - Support for environment variable expansion like
$HOME,$USER
- Input Redirection (
command < file.txt) - Output Redirection (
command > file.txt) - Used
dup2()for redirection - Refactored execution into
executor.cpp
- Support for piping commands (
ls -l | grep txt) - Implemented multi-command pipeline execution with
pipe()and multiplefork()s - Refactored
CommandandParsedCommandto support pipelines - Added separate
executePipeline()function for handling piped commands
- Support for background processes with
&(sleep 10 &) - Print job PID when running in background
- Implemented SIGCHLD handler to avoid zombie processes
- Used non-blocking
waitpid()withWNOHANGfor cleanup - Added proper signal handling with
sigaction()
- Implemented comprehensive signal handling (SIGINT, SIGTSTP, SIGCHLD)
- Shell doesn't terminate on Ctrl+C or Ctrl+Z
- Ensured proper foreground/background state management
- Added zombie process cleanup system
- Created comprehensive test suite with test runner
- Added unit tests for commands, builtins, utils, and executor
- Implemented integration tests for I/O redirection and pipelines
- Added signal handling tests
- Enhanced Makefile with debug/release/sanitize/format targets
- Added test infrastructure with proper pattern rules
- Implemented
historycommand to view command history - Store previous commands in memory
- Save history to
~/.ninxsh_historyfile - Load history from file when shell starts
- Automatic cleanup of oldest commands when history size exceeds limit
- Support for
!!to execute the last command - Support for
!nto execute command number n from history
- Added explicit error state handling to ParsedCommand structure
- Implemented comprehensive DoS protection with input length validation
- Prevented history pollution by validating commands before storage
- Centralized all protection limits in
include/limits.hppheader - Optimized regex compilation with static const patterns
- Standardized test file naming to
test_*.cppconvention - Added boundary condition tests and enhanced error reporting
- Updated documentation with security features and architecture
- Enhanced prompt format: Modern
username@hostname:path$replacing emoji prompt - ANSI colored prompt for interactive terminal sessions
- Username in bold green, hostname in blue, path in yellow
- Smart home directory replacement with
~ - Intelligent path truncation for very long directories
- Smart terminal detection with
isOutputToTerminal() - Plain text prompt when output is piped to prevent formatting issues
- Cross-platform compatibility with robust fallbacks
- Added complete ANSI Colors namespace (16 colors + formatting)
- Comprehensive terminal utility functions (
getCurrentUsername,getCurrentHostname,getCurrentWorkingDir) - Enhanced test coverage for all terminal functionality
- Updated documentation with new terminal features
- Comprehensive quote parsing with new tokenizer in
src/command.cpp - Proper single quote handling (literal strings, no variable expansion)
- Proper double quote handling (allows variable expansion)
- Escape sequence support with backslash (
\) for special characters - Mixed quoting support (single, double, and unquoted in same command)
- Quote-aware environment variable expansion (respects quote context)
- Pipeline support with quoted arguments
- Comprehensive test suite in
tests/test_quotes.cppwith 7 test cases - Fixed
expandPath()to only handle tilde (~) expansion, not environment variables - All tests passing (10/10) including quote handling integration
- Input Length Validation: Commands longer than 4KB are rejected
- Path Length Limits: File paths longer than 2KB are handled gracefully
- String Processing Limits: Environment variable expansion limited to 2KB strings
- Centralized Configuration: All limits defined in
include/limits.hpp - Explicit Error Handling: Clear error messages for rejected input
- History Protection: Invalid commands are not stored in command history
- Static Regex Compilation: Environment variable patterns compiled once
- Early Exit Strategies: Skip expensive operations when possible
- Memory Management: Proper cleanup with RAII patterns
The ninxsh project includes a comprehensive C++ test suite covering all major functionality.
make test # Run complete test suiteThe test suite includes:
- Command parsing - Input tokenization and validation
- Builtin commands - All builtin functions (cd, exit, clear, history, jobs, kill, fg, bg)
- External execution - Fork/exec functionality and process management
- I/O redirection & pipelines - File redirection and command chaining
- Signal handling - Ctrl+C, Ctrl+Z, and process cleanup
- Job management - Background job tracking and control
- History functionality - Command storage and expansion
- DoS protection - Input validation and security limits
- Utility functions - Helper and utility code
All tests use a unified framework and report PASSED/FAILED status.
# Test normal operation
echo "hello world"
cd ~
ls | grep txt
# Test history
history
!!
!1
# Test environment variables
echo $HOME
cd $HOME
# Test background jobs and job control
sleep 30 & # Start background job
jobs # List active jobs
kill 1234 # Kill process by PID
fg 1 # Bring job 1 to foreground
bg 1 # Resume job 1 in background
# Test pipelines with background
ls -la | grep txt & # Run pipeline in background
jobs # View the background pipelineContributions are welcome! Please read CONTRIBUTING.md for guidelines on how to contribute to this project.
- Fork the repository
- Clone your fork locally
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with proper tests
- Test thoroughly (
make test) - Format code (
make format) - Commit with descriptive messages
- Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Special thanks to the following people who have contributed to making ninxsh better:
- @piotrmaciejbednarski - Discovered and reported critical DoS vulnerability in input processing, provided detailed reproduction steps and suggested security improvements that led to the implementation of comprehensive DoS protection in Day 10
Want to see your name here? Check out our Contributing Guidelines and submit a pull request!
- Built with modern C++17 standards
- Inspired by traditional Unix shells (bash, zsh, tcsh)
- Cross-platform compatibility with macOS, Linux, and Windows
- Comprehensive testing and documentation practices
- Issues: Report bugs and request features via GitHub Issues
- Discussions: Join conversations in GitHub Discussions
- Documentation: Full documentation available in this README and CONTRIBUTING.md
ninxsh - A modern Unix shell written in C++
⭐ Star this repo if you find it useful! ⭐
Built with ❤️ by developers, for developers