Skip to content

Bash compatibility

This document details brush’s compatibility with bash, including supported features, known limitations, and how to report issues.

brush aims for high compatibility with bash. We validate this through 1700+ compatibility test cases that compare behavior against bash as an oracle.

Compatibility snapshot: Production-ready for most use cases. Your .bashrc, aliases, functions, and completions should “just work.”

  • if/then/elif/else/fi conditionals
  • for, while, until loops
  • Arithmetic for loops: for ((i=0; i<10; i++))
  • case/esac pattern matching
  • &&, || conditional execution
  • Subshells () and command grouping {}
  • Pipelines and pipeline negation !
  • Coprocesses: coproc { ... } and coproc NAME { ... }
  • Brace expansion: {a,b,c}, {1..10}, {a..z}
  • Parameter expansion: ${var:-default}, ${var:+set}, ${var#pattern}, ${var%pattern}, ${var//find/replace}, etc.
  • Command substitution: $(cmd), `cmd`
  • Arithmetic expansion: $((expr))
  • Process substitution: <(cmd), >(cmd)
  • Tilde expansion: ~, ~user
  • Globbing: *, ?, [...]
  • Extended globbing: ?(pat), *(pat), +(pat), @(pat), !(pat)
  • globstar: ** recursive matching
  • I/O: echo, printf, read, mapfile/readarray
  • Variables: declare, local, export, unset, readonly, typeset
  • Control: break, continue, return, exit
  • Navigation: cd, pushd, popd, dirs, pwd
  • Jobs: jobs, fg, bg, wait, kill
  • Completion: complete, compgen, compopt
  • History: history, fc
  • Testing: test, [, [[
  • Sourcing & introspection: ., source, eval, caller
  • Misc: alias, unalias, hash, type, command, builtin, enable, help, times, ulimit, umask, trap, shopt, set, shift, getopts
  • Indexed arrays: arr=(a b c), ${arr[0]}, ${arr[@]}
  • Associative arrays: declare -A map
  • Array slicing: ${arr[@]:start:length}
  • Array operations: ${#arr[@]}, ${!arr[@]}, ${!arr[*]}
  • Background execution: cmd &
  • Suspend/resume: Ctrl+Z, fg, bg
  • Job listing: jobs
  • Process groups and pipelines
  • RANDOM, SRANDOM
  • LINENO, FUNCNAME, BASH_SOURCE
  • EPOCHSECONDS, EPOCHREALTIME
  • SECONDS
  • PWD, OLDPWD
  • BASH_VERSINFO, BASH_VERSION
  • Compatible with bash-completion
  • Git, Docker, systemctl, etc. completions work out of the box
  • complete, compgen, compopt builtins
  • Standard: >, >>, <, 2>&1
  • Here documents: <<EOF, <<-EOF (tab-stripped), <<< (here strings)
  • File descriptor manipulation: >&n, <&n, n>&m
  • Process substitution redirects: >(cmd), <(cmd)
  • Clobber control: >|, set -o noclobber
Status Feature
DEBUG trap
ERR trap
EXIT trap
🔷 Signal traps (SIGINT, SIGTERM, etc.) — in progress
Status Feature
Basic bind support
bind -x for custom key-bound commands
🔷 Advanced bind features — in progress
Status Feature
Common options: errexit, pipefail, extglob, globstar, noclobber, nounset, failglob
🔷 Less common options — in progress

These features are on our roadmap but not yet implemented:

The select builtin for creating menu-driven scripts is not yet implemented.

Terminal window
# Not yet supported
select opt in "Option A" "Option B" "Quit"; do
case $opt in
"Option A") echo "A";;
"Option B") echo "B";;
"Quit") break;;
esac
done

The wait -n option to wait for the next background job to complete is not implemented.

Terminal window
# Not yet supported
job1 &
job2 &
wait -n # Wait for whichever finishes first

The special variable BASH_COMMAND that contains the currently executing command is currently only available in trap contexts.

These job control builtins are not yet implemented.

These areas have known differences from bash in edge cases. Most users won’t encounter these, but they’re documented for completeness.

There are ~10 known edge cases where IFS word splitting behavior differs from bash, particularly around:

  • Non-whitespace IFS characters and empty field creation
  • Mixed whitespace and non-whitespace IFS
  • Leading/trailing delimiter handling

Some advanced printf format specifiers behave differently (~8 known cases), particularly features not supported by the underlying uucore library.

  • Division by zero handling may differ in errexit mode
  • $(( exit N )) syntax edge case

Some complex alias expansion scenarios differ from bash (see GitHub issues #57, #286).

  • Total test cases: 1700+
  • Known failures: ~125
  • Most failures are edge cases in IFS handling and printf

The test suite runs on every PR and compares behavior against bash as an oracle.

brush targets compatibility with bash 5.3+. Behavior may differ from older bash versions (3.x, 4.x) in some areas.

Found a script that works in bash but not in brush?

  1. Check existing issues: GitHub Issues
  2. Create a minimal reproducer: Reduce to the smallest failing script
  3. File an issue with:
    • The script or command that fails
    • Expected behavior (what bash does)
    • Actual behavior (what brush does)
    • Your platform (Linux/macOS/etc.)
  • GitHub Issues: Track specific compatibility work
  • Test Suite: 1700+ tests run on every PR
  • This Document: Updated as features are implemented

Describes brush v0.4.0. Maintained upstream as docs/reference/compatibility.md; later changes on main may not be reflected here yet.

Last updated:

Open source under the MIT license. Built in the open by the brush community.