Builtin commands now properly respect output redirects by using
file descriptor manipulation before execution.
Test results: 157/192 passing (81.8%) in posix_compliance_test.sh
- Add pipeline negation support (! cmd inverts exit code)
- Fix set builtin to handle -- and positional parameters
- Add POSIX compliance test suite (10 test files)
Test results: 151/192 passing (78.6%) in posix_compliance_test.sh
- Enable quick_completions: auto-selects when only one match exists
- Enable partial_completions: fills common prefix for multiple matches
Now 'cd tes<Tab>' completes directly to 'cd tests/' instead of
showing a single-item menu.
- Up/Down arrows navigate vertically in the completion menu
- Left/Right arrows navigate horizontally
- Shift+Tab cycles backwards through completions
- Arrow keys fall back to normal cursor movement when menu is closed
- Enter selects completion or submits command
Add three new shell builtins:
- printf: Formatted output with %s, %d, %x, %o, %c, %b, %q specifiers
and escape sequences (\n, \t, \xHH, \0nnn)
- mapfile/readarray: Read lines from stdin into an indexed array
with options -d (delimiter), -n (count), -O (origin), -s (skip), -t (trim)
- disown: Remove jobs from job control with -a (all), -r (running), -h flags
Also adds supporting methods to JobList: current_job_id(), previous_job(),
disown_job(), and disown_all().
Add full support for the bash select loop:
- Grammar rule for select var in words; do body; done
- SelectStatement AST node with var_name, words, and body
- Parser implementation for select statements
- Executor with menu display, PS3 prompt, REPLY variable
The select loop displays a numbered menu, reads user selection,
sets the loop variable to the selected item, and executes the body
until break is called or EOF is reached.
Add support for:
- ${!var} indirect expansion (use var's value as variable name)
- ${var@Q} quote value for reuse as input
- ${var@E} expand escape sequences
- ${var@U} uppercase all / ${var@u} uppercase first
- ${var@L} lowercase all
- ${var@P}, ${var@A}, ${var@K}, ${var@a} (basic support)
Grammar updates:
- Add ${!var} rule (distinct from ${!arr[@]} for array indices)
- Add @op modifier for transformation operators
Add 7 tests for new expansion features
Add comma operator support to arithmetic expressions:
- Tokenizer recognizes ',' as Comma token
- Add parse_comma() as lowest precedence level (below ternary)
- Evaluates expressions left-to-right, returns last value
- Common pattern for side effects: (a=1, b=2, a+b)
- Add 5 tests for comma operator behavior
Add proper lvalue tracking to arithmetic expansion, enabling assignments
and increment/decrement operators to actually modify variables.
Changes:
- Add LValue enum to distinguish values from variable references
- All parse methods now return LValue, preserving variable identity
- Assignment operators (=, +=, -=, *=, /=, %=, **=) store results
- Pre-increment/decrement (++x, --x) modify and return new value
- Post-increment/decrement (x++, x--) return old value and modify
- NotAnLvalue error for invalid assignments (e.g., 5++, (x)=1)
Examples now work correctly:
x=5; echo $((x += 3)) # outputs 8, x is now 8
x=5; echo $((++x)) # outputs 6, x is now 6
x=5; echo $((x++)) # outputs 5, x is now 6
a = b = c = 5 # chained assignment works
29 comprehensive tests added covering all lvalue operations.
Add support for the coproc builtin which allows bidirectional
communication with background processes using pipes.
Features:
- Create coproc with custom or default name (COPROC)
- Two pipes for bidirectional I/O (stdin/stdout)
- File descriptors exposed as array variables (NAME[0], NAME[1])
- Integration with job control system
- Proper process group management
Implementation:
- Add CoprocState struct to rush_expand::Context
- Add builtin_coproc() in rush-executor/command.rs
- Helper methods: set_coproc_vars() and clear_coproc()
- Automatic cleanup on coproc replacement
Usage:
coproc [NAME] command [args...]
Example:
coproc grep pattern
echo "test" >&${COPROC[1]}
read result <&${COPROC[0]}
Implement full process substitution support using named pipes (FIFOs)
for <(command) and >(command) syntax, enabling advanced piping patterns.
Core Implementation (process_subst.rs):
- Create named FIFOs in /tmp with unique names (rush-psub-{pid}-{counter})
- Fork child processes to execute commands in background
- Connect command stdout/stdin to FIFOs using dup2()
- Global registry tracks active FIFOs and PIDs for cleanup
- Thread-safe Mutex-based registry with proper cleanup handlers
Process Substitution Input <(command):
- Creates FIFO that provides command's stdout as readable file
- Forks child with stdout redirected to FIFO write end
- Parent opens FIFO read end and uses as stdin source
- Example: diff <(ls dir1) <(ls dir2)
Process Substitution Output >(command):
- Creates FIFO that feeds into command's stdin as writable file
- Forks child with stdin redirected from FIFO read end
- Parent opens FIFO write end and uses as stdout target
- Example: tee >(gzip > file.gz) >(bzip2 > file.bz2)
Integration:
- Update redirect.rs to handle ProcessSubstInput/ProcessSubstOutput
- Open FIFOs as file handles for stdin/stdout redirection
- Use command_executor callback for proper context handling
- Fallback to "sh -c" if executor not available
Cleanup Handlers:
- Register atexit() handler in CLI to cleanup all FIFOs on exit
- wait_for_all() function for non-blocking child reaping
- Automatic FIFO removal when processes complete
- Proper resource cleanup on fork failures
Platform Support:
- Full implementation on Unix-like systems (Linux, macOS, BSD)
- Uses nix crate for safe syscall wrappers (mkfifo, fork, dup2)
- Graceful error messages on non-Unix platforms
- Added nix dependency to rush-cli
Testing:
- Verified grammar and AST parsing of process substitution syntax
- Confirmed FIFO creation and cleanup mechanisms
- Validated fork/exec process management
This enables advanced bash-compatible patterns like:
diff <(sort file1) <(sort file2)
cat file | tee >(wc -l) >(grep pattern)
comm <(ls dir1) <(ls dir2)
Add parsing infrastructure for process substitution syntax <(command)
and >(command), preparing for FIFO-based implementation.
Grammar Changes:
- Add process_subst_input rule for <(command) syntax
- Add process_subst_output rule for >(command) syntax
- Reuse command_subst_content for parsing command strings
- Position rules before standard redirects to match correctly
AST Changes:
- Add ProcessSubstInput variant to Redirect enum
- Add ProcessSubstOutput variant to Redirect enum
- Store command string for later execution
Parser Integration:
- Handle process_subst_input and process_subst_output rules
- Reconstruct command content using existing infrastructure
- Properly handle nested parentheses in commands
Redirect Stubs:
- Add placeholder implementations in redirect.rs
- Return "not yet implemented" errors for now
- Will be replaced with FIFO-based implementation
This commit establishes the foundation for process substitution.
The actual FIFO creation and process forking will be implemented
in the process_subst.rs module in the next commit.
Fix broken !(pattern) extended glob operator and add comprehensive
support for extended glob patterns in both grammar and expansion.
Core Implementation:
- Add ExtGlobPattern enum with Standard and Negation variants
- Implement two-stage filtering: expand base pattern, then exclude matches
- Convert glob patterns to regex for precise negation matching
- Add glob_to_regex() function for pattern conversion
Grammar Changes:
- Add extglob_pattern rule to parse !(pat), ?(pat), *(pat), +(pat), @(pat)
- Treat extended globs as word parts for proper tokenization
- Support extglob patterns in all word contexts
Parser Integration:
- Handle extglob_pattern rule in parse_word_part()
- Convert extended globs to literal strings for later expansion
Glob Expansion:
- Replace broken "*" conversion for !(pattern) with proper negation logic
- Implement expand_standard_glob() for non-negated patterns
- Add has_glob_chars() detection for extended glob operators
- Wire extglob option from ShellOptions through GlobOptions
Option Handling:
- Add extglob field to GlobOptions (default: false)
- Check context.options.extglob before processing extended patterns
- Skip extended glob processing when option is disabled
Testing:
- Verified !(*.txt) correctly excludes .txt files
- Verified !(file.*) correctly excludes files starting with "file."
- Verified extglob option toggle works correctly
- Confirmed bash-compatible negation behavior
Example:
shopt -s extglob
echo !(*.txt) # Outputs: file.log file.md file.rs (excludes .txt files)
The !(pattern) operator now works correctly with proper two-stage
filtering instead of the broken "*" approximation.
Add full support for associative arrays (bash-compatible hash maps) and
the declare/typeset builtin for array and variable declarations.
Core Changes:
- Add ArrayType enum to distinguish indexed vs associative arrays
- Modify Context.arrays to HashMap<String, ArrayType>
- Add helper methods for array operations (get/set/length/type-checking)
Declare Builtin:
- Support flags: -a (indexed), -A (associative), -r (readonly), -x (export), -p (print)
- Implement array creation and attribute management
- Add proper print formatting for both array types
Variable Expansion:
- Update expand_var() to handle both array types
- Support ${arr[key]} for associative arrays
- Support ${arr[index]} for indexed arrays
- Maintain backward compatibility for indexed arrays
Pipeline Integration:
- Update array assignment handling in pipeline.rs
- Use Context helper methods for type-safe array operations
- Support both indexed and associative array element assignments
Testing:
- Verified array creation with declare -a/-A
- Verified element access and assignment
- Verified array lengths and expansions
- Verified declare -p output formatting
Implements proper command substitution using rush's own parser
and executor instead of falling back to sh -c.
Changes:
- Add command_subst_exec.rs with dedicated executor using pipes
- Add CommandExecutorWrapper to Context for pluggable execution
- Remove sh -c fallback (now fully bespoke implementation)
- Fix double-close file descriptor bug in pipe handling
- Integrate internal executor in REPL and CLI contexts
Command substitution now works recursively and properly
handles nested substitutions.
Completes readonly implementation with full enforcement:
Changes:
- Modified Context::set_var() to return Result<(), String>
- Returns Err(name) if variable is readonly
- Updated all set_var call sites to handle Result
- Added readonly checks in pipeline variable assignments
- Added readonly checks in for loop variable assignments
- Updated all tests to use .unwrap()
Enforcement:
- Variable assignments fail with "readonly variable" error
- Error occurs in pipeline.rs and control_flow.rs
- Prevents modification of readonly vars throughout execution
- Works with export and regular assignments
Testing:
- Readonly variables now properly protected
- Attempting to modify returns permission denied error
- Listing with readonly -p shows all protected vars
This completes the readonly builtin as a full POSIX special builtin.
Adds readonly variable tracking infrastructure:
Features:
- readonly: list all readonly variables
- readonly -p: print in reusable format
- readonly VAR=value: set and mark readonly
- readonly VAR: mark existing variable readonly
- Tracks readonly status in Context
Implementation:
- Added readonly_vars HashSet to Context
- Added mark_readonly(), is_readonly(), readonly_vars() methods
- Implemented builtin_readonly function
- Lists readonly vars, prevents double-readonly assignment
Limitation:
- Enforcement during assignment not yet implemented
- Variable assignments don't check readonly status yet
- Need to add checking in pipeline execution layer
This is a POSIX special builtin for variable protection.
Adds unset functionality for removing variables and functions:
Features:
- unset NAME...: unset variables and functions
- unset -v NAME...: unset only variables
- unset -f NAME...: unset only functions
- Handles multiple names in one call
- No error if variable/function doesn't exist (POSIX)
Implementation:
- Added builtin_unset function
- Uses context.unset_var() for variables
- Removes from context.functions for functions
- By default, unsets both vars and funcs
- Flags -v/-f restrict to one type
This is a POSIX special builtin, essential for:
- Cleaning up temporary variables
- Removing functions from environment
- Script cleanup and reset operations
Adds export functionality for environment variable management:
Features:
- export: list all exported variables
- export -p: print in reusable format
- export VAR=value: set and export variable
- export VAR: export existing variable
- export -n VAR: unexport variable (keep as local)
Implementation:
- Added builtin_export function
- Added unexport_var method to Context
- Handles assignment and non-assignment forms
- Lists all exported vars when called without args
- Proper error handling for invalid options
This is a POSIX special builtin, essential for:
- Setting environment variables for child processes
- Configuration in .bashrc/.profile
- Script environment management