Version: 1.5.0
Complete feature reference for the Lush shell
- Overview
- Shell Modes
- Line Editing (LLE)
- Extended Syntax
- Completion System
- Hook System
- Debugging
- Theme System
- Configuration
- Builtin Commands
- POSIX Compliance
- Scripting
Lush v1.5.0 is an advanced interactive shell with three distinguishing capabilities:
- LLE (Lush Line Editor): A native line editor with Emacs keybindings, context-aware completions, and syntax highlighting - not a readline wrapper
- Multi-Mode Architecture: Run in POSIX, Bash, Zsh, or Lush mode depending on your needs
- Integrated Debugging: Set breakpoints, step through code, inspect variables, and profile performance from within the shell
# Interactive shell
lush
# Execute a command
lush -c "echo 'Hello'"
# Run a script
lush script.sh
# Specific mode
lush --posix script.sh
lush --bash script.sh| Option | Description |
|---|---|
-c COMMAND |
Execute command string and exit |
-i |
Force interactive mode |
-l |
Act as login shell |
-s |
Read commands from standard input |
-e |
Exit on error (set -e) |
-x |
Trace execution (set -x) |
-u |
Error on unset variables (set -u) |
-v |
Verbose mode (set -v) |
-f |
Disable pathname expansion (set -f) |
--posix |
POSIX compliance mode |
--bash |
Bash compatibility mode |
--zsh |
Zsh compatibility mode |
--help |
Show help message |
--version |
Show version information |
Lush operates in one of four modes, each with different feature sets and behaviors.
The recommended mode for interactive use and Lush-specific scripts. All features enabled:
- Extended syntax (arrays,
[[]], process substitution) - Bash and Zsh compatible features
- Hook system (precmd, preexec, chpwd, periodic)
- Full LLE capabilities
- Integrated debugging
set -o lush # Enable (usually default)Strict POSIX sh compliance for maximum portability:
- No arrays
- No
[[]](use[ ]) - No process substitution
- No extended globbing
- POSIX-defined behavior only
set -o posix # Enable POSIX mode
lush --posix # Start in POSIX modeBash-compatible feature set:
- Indexed arrays:
arr=(a b c) - Associative arrays:
declare -A - Extended test:
[[]] - Process substitution:
<(),>() - Extended parameter expansion
- Bash-style brace expansion
set -o bash # Enable Bash modeZsh-compatible feature set:
- All Bash features
- Glob qualifiers:
*(.),*(/),*(@) - Extended parameter expansion forms
- Zsh-style option names
set -o zsh # Enable Zsh mode| Feature | POSIX | Bash | Zsh | Lush |
|---|---|---|---|---|
| Basic syntax | Yes | Yes | Yes | Yes |
| Indexed arrays | No | Yes | Yes | Yes |
| Associative arrays | No | Yes | Yes | Yes |
[[]] extended test |
No | Yes | Yes | Yes |
| Process substitution | No | Yes | Yes | Yes |
| Extended globbing | No | Yes | Yes | Yes |
| Glob qualifiers | No | No | Yes | Yes |
| Hook functions | No | No | Partial | Yes |
| Plugin system | No | No | No | Yes |
LLE is Lush's native line editor. It is not based on GNU Readline or any external library.
LLE provides complete Emacs-style editing:
Movement
| Keybinding | Action |
|---|---|
Ctrl-A |
Beginning of line |
Ctrl-E |
End of line |
Ctrl-B |
Backward one character |
Ctrl-F |
Forward one character |
Alt-B |
Backward one word |
Alt-F |
Forward one word |
Editing
| Keybinding | Action |
|---|---|
Ctrl-D |
Delete character (or EOF on empty line) |
Backspace |
Delete backward |
Ctrl-K |
Kill to end of line |
Ctrl-U |
Kill to beginning of line |
Ctrl-W |
Kill previous word |
Alt-D |
Kill next word |
Ctrl-Y |
Yank (paste) killed text |
Alt-Y |
Yank-pop (cycle through kill ring) |
Ctrl-T |
Transpose characters |
Alt-T |
Transpose words |
Ctrl-_ |
Undo |
History
| Keybinding | Action |
|---|---|
Ctrl-P / Up |
Previous history entry |
Ctrl-N / Down |
Next history entry |
Ctrl-R |
Reverse incremental search |
Alt-< |
Beginning of history |
Alt-> |
End of history |
Completion
| Keybinding | Action |
|---|---|
Tab |
Complete |
Alt-? |
List completions |
Alt-* |
Insert all completions |
Control
| Keybinding | Action |
|---|---|
Ctrl-L |
Clear screen |
Ctrl-C |
Interrupt (SIGINT) |
Ctrl-Z |
Suspend (SIGTSTP) |
Ctrl-D |
EOF (on empty line) |
A vi-mode framework is in place but not yet feature-complete. Track progress in docs/lle_specification/ and the GitHub issue tracker. For now, emacs mode is the supported flow.
LLE highlights your input in real-time:
- Commands: Valid commands highlighted, unknown commands indicated
- Builtins: Distinguished from external commands
- Strings: Single and double quotes
- Variables:
$var,${var},$(...) - Operators: Pipes, redirections, logic operators
- Comments:
# comment - Paths: Path-shaped arguments are classified by shape (absolute
/..., relative./..., home~/...) and kind (regular file vs. directory vs. missing). Defaults: directories underlined green, missing paths red, regular files unhighlighted (let the line itself speak). Themes can color every shape x kind slot independently or override the kind-onlypath_file/path_dirfallbacks. See the[syntax]section inexamples/theme.toml. - Errors: Syntax errors indicated before execution
Incomplete commands continue naturally with proper indentation:
$ for i in 1 2 3; do
> echo "Number: $i"
> doneLLE exposes three first-class customization surfaces, each driven by its own builtin. They route through the same central configuration registry as every other setting (see CONFIGURATION.md).
display lle widget -- user-defined editing actions
Bind a shell function to a key sequence so it acts as a one-shot
editing operation. Equivalent to zle -N in zsh, but works in every
mode.
my-uppercase-word() {
# widget body operates on the active LLE buffer
lle buffer transform "${WIDGET_WORD}" upper
}
display lle widget add my-uppercase-word
display lle widget bind '\eU' my-uppercase-worddisplay lle hook -- lifecycle hooks
precmd, preexec, chpwd, and periodic hook functions can also
be registered with display lle hook for symmetric configuration
alongside widgets and segments.
display lle segment -- custom prompt segments
Define a prompt segment that renders inline in the powerline-style themes. Segments are pure functions of shell state and re-render on prompt redraw.
display lle segment add kube_ctx '
[[ -n "${KUBECONFIG:-}" ]] && echo "k8s:$(basename "$KUBECONFIG")"
'
display lle segment enable kube_ctxSee LLE_GUIDE.md for the full reference.
Lush mode (and Bash/Zsh modes) support extended shell syntax beyond POSIX.
Lush stores indexed and associative arrays as first-class List
and Map value kinds (SEMANTICS section 7). They share the
per-scope symbol table with scalars, so [[ -v name ]], unset name, and function-local declarations work identically for all
three kinds.
Indexed Arrays (Lists)
# Declaration
fruits=(apple banana cherry)
declare -a numbers
# Function-local list literal
f() { local arr=(a b c); echo "${arr[1]}"; }
# Indexed assignment
fruits[0]="apple"
fruits[3]="date"
# Access -- always use a presentation operator
echo "${fruits[0]}" # First element
echo "${fruits[@]}" # All elements as separate words
echo "${fruits[*]}" # All elements joined by IFS
echo "${#fruits[@]}" # Count
echo "${!fruits[@]}" # Indices
# Slicing
echo "${fruits[@]:1:2}" # Elements 1-2Bare ${fruits} is a type error. SEMANTICS section 3.9
forbids using a List as a Scalar without a presentation operator
([@], [*], [N]). The shell raises SHELL_ERR_TYPE_MISMATCH
with a structured-error citation. There is no implicit "first
element" fallback. Static analysis (debug analyze / lint) flags
this before runtime.
Associative Arrays (Maps)
declare -A colors # Declaration required
colors[apple]="red"
colors[banana]="yellow"
colors[cherry]="red"
echo "${colors[apple]}" # red
echo "${colors[@]}" # All values
echo "${!colors[@]}" # All keys (bash spelling)
echo "${(k)colors}" # All keys (zsh spelling, same engine)Scope and Lifetime
arr=(global)
f() {
local arr=(local) # shadows global for this frame
echo "${arr[0]}" # local
}
f
echo "${arr[0]}" # global
unset arr # removes binding from scope
[[ -v arr ]] && echo set || echo unset # unsetArithmetic Expansion
result=$((5 + 3))
result=$((count * 2))
result=$((a > b ? a : b))Arithmetic Command
(( count++ ))
(( total += value ))
if (( count > 10 )); then
echo "Over ten"
filet Builtin
let count=count+1
let "total = a + b"Operators
| Operator | Description |
|---|---|
+ - * / |
Basic arithmetic |
% |
Modulo |
** |
Exponentiation |
++ -- |
Increment/decrement |
<< >> |
Bit shift |
& | ^ |
Bitwise AND, OR, XOR |
~ |
Bitwise NOT |
< > <= >= |
Comparison |
== != |
Equality |
&& |
Logical AND |
|| |
Logical OR |
? : |
Ternary |
The [[]] construct provides enhanced testing:
# Pattern matching
[[ $file == *.sh ]]
# Regex matching
[[ $email =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]
# Logical operators
[[ -f $file && -r $file ]]
[[ $a == $b || $a == $c ]]
# Negation
[[ ! -d $dir ]]
# String comparison
[[ $str1 < $str2 ]] # LexicographicTreat command output as a file:
# Compare outputs
diff <(ls dir1) <(ls dir2)
# Feed to command expecting file
wc -l <(find . -name "*.c")
# Write to process
tee >(gzip > file.gz) >(wc -l > count.txt)Case Modification
name="hello world"
echo "${name^}" # Hello world (first char upper)
echo "${name^^}" # HELLO WORLD (all upper)
echo "${name,}" # hello world (first char lower)
echo "${name,,}" # hello world (all lower)Substitution
path="/home/user/file.txt"
echo "${path/user/admin}" # Replace first
echo "${path//\//\\}" # Replace all
echo "${path/#\/home/~}" # Replace at start
echo "${path/%.txt/.md}" # Replace at endSubstring
str="Hello World"
echo "${str:0:5}" # Hello
echo "${str:6}" # World
echo "${str: -5}" # World (note space)
echo "${str:0:-6}" # HelloLength and Removal
echo "${#str}" # 11 (length)
echo "${path#*/}" # home/user/file.txt (remove shortest prefix)
echo "${path##*/}" # file.txt (remove longest prefix)
echo "${path%/*}" # /home/user (remove shortest suffix)
echo "${path%%/*}" # (empty, remove longest suffix)Indirect Expansion
name="PATH"
echo "${!name}" # Value of $PATHTransformation
str="hello"
echo "${str@Q}" # Quoted: 'hello'
echo "${str@E}" # Escape sequences expanded
echo "${str@P}" # Prompt expansion
echo "${str@A}" # Assignment form: str='hello'
echo "${str@a}" # Attributes (for declared vars)Zsh Parameter Flags
Bash-style transformations have zsh-flag spellings that route to the
same engine. The flags are accepted in all modes -- lush does not
gate them behind mode zsh.
var=hello
echo "${(U)var}" # HELLO (same as ${var^^})
echo "${(L)var}" # hello (same as ${var,,})
echo "${(C)var}" # Hello (title-case first char)
arr=(c a b)
echo "${(o)arr}" # a b c (sorted)
declare -A m=([a]=1 [b]=2)
echo "${(k)m}" # keys (same as ${!m[@]})See EXTENDED_SYNTAX.md for the full flag catalog.
Enable with shopt -s extglob:
# Zero or one match
ls ?(pattern)
# Zero or more matches
ls *(pattern)
# One or more matches
ls +(pattern)
# Exactly one (negated)
ls @(pattern)
# Anything except
ls !(pattern)
# Examples
ls *.+(c|h) # .c or .h files
ls !(*.o|*.a) # Not .o or .a filesFilter glob results by file attributes:
ls *(.) # Regular files only
ls *(/) # Directories only
ls *(@) # Symbolic links only
ls *(*) # Executable files onlyExtended case statement patterns:
case "$opt" in
-v|--verbose)
verbose=1
;& # Fall through to next
-d|--debug)
debug=1
;;
-*)
;;& # Continue pattern testing
*)
echo "Unknown"
;;
esacInteractive menu:
select choice in "Option 1" "Option 2" "Quit"; do
case $choice in
"Option 1") echo "First" ;;
"Option 2") echo "Second" ;;
"Quit") break ;;
esac
doneMeasure execution time:
time sleep 1
time { cmd1; cmd2; cmd3; }LLE provides context-aware completions.
- Command completion: Executables in
$PATH - Builtin completion: Every shell builtin (~60 distinct commands; see BUILTIN_COMMANDS.md)
- File completion: Paths and filenames
- Variable completion:
$VARnames - Option completion: Command-specific options
- Debug-prompt completion: At
(lush-debug), completes the break-prompt command vocabulary instead of normal first-word candidates
Every completable builtin understands its arguments:
set -o <Tab> # Shows all shell options
config set <Tab> # Shows config sections
debug <Tab> # Shows: on off vars print trace profile analyze ...
display <Tab> # Shows: lle features themes status widget hook segment
theme <Tab> # Shows: list set show infocommand<Tab> # Complete command name
/path/to/<Tab> # Complete path
$VAR<Tab> # Complete variable name
cmd --<Tab> # Complete long options
cmd -<Tab> # Complete short optionsLush provides Zsh-style hook functions.
precmd
Runs after each command completes, before the prompt is displayed:
precmd() {
# Update terminal title
echo -ne "\033]0;$(pwd)\007"
}preexec
Runs after a command is entered but before it executes:
preexec() {
# Log command
echo "$(date): $1" >> ~/.command_log
}chpwd
Runs when the current directory changes:
chpwd() {
# Auto-ls on directory change
ls
}periodic
Runs periodically (interval set by PERIOD variable):
PERIOD=60 # Every 60 seconds
periodic() {
# Check for new mail
check_mail
}For multiple handlers on the same event:
# Add functions to arrays
precmd_functions+=(my_precmd_handler)
preexec_functions+=(my_preexec_handler)
chpwd_functions+=(my_chpwd_handler)
periodic_functions+=(my_periodic_handler)
# Functions are called in order
my_precmd_handler() {
echo "First handler"
}
another_precmd_handler() {
echo "Second handler"
}
precmd_functions=(my_precmd_handler another_precmd_handler)Lush is the only shell with integrated interactive debugging -- breakpoints, depth-aware stepping, kind-aware variable inspection, and predictive static analysis built into the binary. The contract is held by PHILOSOPHY section 7: no language change is "done" until the debugger can still see it.
debug on # Basic debugging
debug on 2 # Detailed tracing
debug on 3 # Maximum verbosity
debug off # Disabledebug vars # All variables with kind labels (Scalar/List/Map)
debug print VAR # Render contents formatted by kind
debug functions # List defined functionsAt the (lush-debug) break prompt, type <name> / t <name>
print just the kind label without rendering contents.
debug break add script.sh 12 # halt at line 12
debug break list
source script.sh # breakpoint fires
# (lush-debug) prompt -- LLE-driven, with history+Ctrl-R+completion
# next depth-aware step over
# out step out of current function frame
# continue resumedebug analyze ./script.sh # flags type/error/warning/style/portability
analyze ./script.sh # same engine, standalone builtin
lint ./script.shThe type category catches SEMANTICS section 3.9 type-mismatch
sites (e.g. bare ${arr} where arr is a List) at edit time.
debug trace on # Enable tracing
debug trace off # Disable tracingERR / DEBUG / RETURN trap inheritance into function bodies is
available via set -o errtrace and set -o functrace.
debug profile on
# Run commands to profile
debug profile report # Show timing information
debug profile offFor the complete reference, see DEBUGGER_GUIDE.md.
Lush includes a professional theme system.
- default: Clean, minimalist
- corporate: Professional business
- dark: High contrast dark
- colorful: Vibrant development
- minimal: Ultra-clean
- classic: Traditional shell
theme list # List all themes
theme show # Current theme details
theme set <name> # Switch themes
theme info <name> # Theme information
theme colors # Show color palettePrompts show git status when in a repository:
[user@host] ~/project (main) $ # Current branch
[user@host] ~/project (main +) $ # Uncommitted changes
[user@host] ~/project (main !) $ # Staged changestheme symbols unicode # Unicode symbols
theme symbols ascii # ASCII fallback
theme symbols auto # Auto-detect (default)Lush v1.5.0 uses a unified TOML-based configuration system with XDG Base Directory compliance.
~/.config/lush/
+-- lushrc.toml # Main configuration (TOML format)
+-- lushrc # Optional shell script (sourced after lushrc.toml)
+-- themes/ # Theme files
The shell respects $XDG_CONFIG_HOME if set, otherwise uses ~/.config.
# View configuration
config show # All sections
config show shell # Shell options
config show completion # Completion settings
config show display # Display settings
# Get values
config get shell.errexit
# Set values
config set shell.errexit true
config set completion.enabled true
config set display.syntax_highlighting true
# Save to file
config save
# Reset defaults
config reset
# Migrate from legacy format
config migrateMultiple syntax options for setting shell options:
# setopt/unsetopt (recommended, user-friendly)
setopt errexit # Enable exit-on-error
setopt extglob # Enable extended globbing
unsetopt xtrace # Disable tracing
# config command
config set shell.errexit true
# Traditional POSIX
set -e
set -o errexitChanges persist when you run config save.
Human-readable TOML format:
# ~/.config/lush/lushrc.toml
[shell]
mode = "lush"
errexit = false
nounset = false
xtrace = false
# Feature overrides (extended_glob, etc.) are mode-derived, not TOML keys:
# set them with `setopt`/`shopt` in the rc script (~/.config/lush/lushrc),
# e.g. `setopt extended_glob`. They are never written to or read from this file.
[history]
enabled = true
size = 10000
file = "~/.local/share/lush/history"
[display]
syntax_highlighting = true
autosuggestions = true
transient_prompt = false
[prompt]
theme = "default"
[completion]
enabled = true
fuzzy = true
case_sensitive = falseOptional power-user escape hatch, sourced after lushrc.toml:
# ~/.config/lush/lushrc
# Aliases
alias ll='ls -la'
alias gs='git status'
# Functions
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Hooks
precmd() {
# Runs before each prompt
}
# Override TOML settings if needed
setopt extglobIf you have an existing ~/.lushrc file, Lush will load it and display a migration notice:
lush: Loading configuration from ~/.lushrc (legacy location)
lush: Run 'config save' to migrate to ~/.config/lush/lushrc.toml
After running config save, your settings are migrated to the new location.
export XDG_CONFIG_HOME=~/.config # Config directory (default)
export LUSH_THEME=dark
export LUSH_DEBUG=1
export LUSH_DISPLAY_OPTIMIZATION=2Lush provides 50 builtin commands.
: . alias bg break
cd command continue eval exec
exit export false fc fg
getopts hash jobs kill pwd
read readonly return set shift
times trap true type ulimit
umask unalias unset wait
declare local let printf source
test [ [[
config display debug network setopt
theme unsetopt help
config - Configuration management
config show [section]
config get <key>
config set <key> <value>
config save
config resetdebug - Integrated debugger
debug on [level]
debug off
debug vars
debug print <var>
debug trace on|off
debug profile on|off|reportdisplay - Display system control
display status
display lle diagnostics
display features
display themes
display statssetopt / unsetopt - Enable/disable shell options
setopt # List all options
setopt errexit # Enable option
setopt extglob # Enable extended globbing
setopt -q extglob # Query silently
unsetopt xtrace # Disable optionFor complete builtin documentation, see BUILTIN_COMMANDS.md.
Lush implements all 24 POSIX shell options.
| Option | Short | Description |
|---|---|---|
allexport |
-a |
Export all variables |
errexit |
-e |
Exit on command failure |
hashall |
-h |
Hash command locations |
ignoreeof |
Require explicit exit | |
interactive |
-i |
Interactive mode |
monitor |
-m |
Job control |
noclobber |
-C |
Don't overwrite files |
noexec |
-n |
Read but don't execute |
noglob |
-f |
Disable globbing |
nolog |
Don't log functions | |
notify |
-b |
Report job status immediately |
nounset |
-u |
Error on unset variables |
pipefail |
Pipeline failure detection | |
posix |
POSIX compliance mode | |
privileged |
-p |
Privileged mode |
verbose |
-v |
Print input lines |
vi |
Vi editing mode | |
xtrace |
-x |
Trace execution |
# Short form
set -e -u -x
# Combined
set -eux
# Long form
set -o errexit
set -o nounset
set -o xtrace
# Config form
config set shell.errexit true
# Disable
set +e
set +o errexit
config set shell.errexit false#!/usr/bin/env lush
# Strict mode (recommended)
set -euo pipefail
# Your script here# Exit on error
set -e
# Trap errors
trap 'echo "Error on line $LINENO"; exit 1' ERR
# Check commands
if ! command -v git >/dev/null 2>&1; then
echo "git required" >&2
exit 1
fi# Standard function
my_function() {
local arg="$1"
echo "Processing: $arg"
}
# With nameref (Lush mode)
swap() {
local -n ref1=$1
local -n ref2=$2
local tmp=$ref1
ref1=$ref2
ref2=$tmp
}For maximum portability, use POSIX mode:
#!/usr/bin/env lush
set -o posix
# POSIX-only constructs
# No arrays, no [[]], no process substitution#!/usr/bin/env lush
# Debug the whole script
debug on 2
# Or debug specific sections
process_data() {
debug on
# complex logic
debug off
}
# Profile performance
debug profile on
expensive_operation
debug profile report
debug profile off| Document | Description |
|---|---|
| LLE_GUIDE.md | Complete LLE documentation |
| EXTENDED_SYNTAX.md | Detailed syntax reference |
| CONFIGURATION.md | Modes, options, registry -- all four configuration surfaces |
| DEBUGGER_GUIDE.md | Debugging reference |
| BUILTIN_COMMANDS.md | All 50 builtins |
| HOOKS_AND_PLUGINS.md | Hook system |