Skip to main content

Module shell_exec

Module shell_exec 

Source
Expand description

Shell command executor for zshrs

Executes the parsed shell AST via fusevm bytecodes. Builtins are dispatched through fusevm’s CallBuiltin mechanism, with handlers accessing executor state via thread-local.

Structs§

AutoloadFlags
Flags for autoloaded functions
CompGroup
Completion group for organizing matches
CompInitBgResult
Result from background compinit thread
CompMatch
A single completion match for zsh-style completion
CompSpec
A completion specification for the complete builtin
CompState
zsh completion state (compstate associative array)
ForkFlags
Flags for zfork()
Intercept
An intercept registration.
ProfileEntry
Profiling entry for zprof
ScheduledCommand
Scheduled command for sched builtin
ShellExecutor
SubshellFlags
Flags for entersubsh()
SubshellSnapshot
Snapshot of subshell-isolated state. Captured at ( entry, restored at ) exit. zsh subshell semantics: assignments inside (…) don’t leak to the outer scope — and that includes export. zsh forks a child for the subshell so the child’s env::set_var dies with the child; without a fork (zshrs runs subshells in-process for perf), we snapshot+restore the OS env table around the subshell. Otherwise (export y=v) would leak y to the parent shell, breaking every script that uses a subshell to scope an env override.
UnixSocketState
Unix domain socket state
VarAttr
Variable attribute record for (t) flag introspection. Mirrors the type+flag bitmask zsh tracks per Param. Each instance picks exactly one base kind plus zero-or-more attribute markers.
ZStyle
zstyle entry for completion configuration
ZptyState
State for a zpty pseudo-terminal
ZshrsHost
ShellHost implementation that delegates to the current ShellExecutor via the with_executor thread-local.

Enums§

AdviceKind
AOP advice type — before, after, or around.
BuiltinType
Builtin command type
ForkResult
Result of fork operation
LoopSignal
Cross-VM loop-control signal. When break/continue is hit inside a body that runs on a sub-VM (e.g. select’s body), the inline patches mechanism can’t reach the outer loop — set this flag and the outer-loop builtin drains it after each iteration.
RedirMode
Redirection mode
VarKind

Constants§

BUILTIN_APPEND_ARRAY
arr+=(d e f) — append N elements to an existing indexed array. Compile emits N element pushes + name push, then CallBuiltin(295, N+1). Handler drains args (last popped = name), extends executor.arrays[name] (creates the entry if missing). Mirrors zsh’s += semantics for indexed arrays.
BUILTIN_APPEND_ASSOC
m[k]+=value — append onto an existing assoc-array value (string concat). If the key doesn’t exist, behaves like SET_ASSOC. Stack: [name, key, value].
BUILTIN_APPEND_SCALAR_OR_PUSH
name+=val (no parens) — runtime-dispatched append. If name is an indexed array → push val as element. If name is an assoc array → error (zsh requires (k v) form). Else → scalar concat (existing SET_VAR behavior).
BUILTIN_ARITH_EVAL
$((expr)) arithmetic substitution. Pops [expr_string], evaluates via the executor’s MathEval (integer-aware), returns result as Value::Str. Bypasses ArithCompiler’s float-only Op::Div path so $((10/3)) returns “3” not “3.333…”.
BUILTIN_ARRAY_ALL
${arr[@]} — splice all elements as a Value::Array. Pops one arg: name. The Array gets flattened by Op::Exec/ExecBg/CallFunction into argv.
BUILTIN_ARRAY_FLATTEN
Flatten one level of Value::Array nesting. Pops N values; for each, if it’s a Value::Array, its elements are appended directly; otherwise the value is appended as-is. Pushes a single Value::Array of the flattened result. Used by the for-loop word-list compile path: when a word like ${arr[@]} produces a nested Array, this lets for i in ${arr[@]} iterate over the inner elements rather than the outer single-element array.
BUILTIN_ARRAY_INDEX
${arr[idx]} — single-element array index. Pops two args: stack: [name, idx_str] Returns the indexed element as Value::str. Indexing semantics: zsh is 1-based by default; bash is 0-based. We follow zsh. Special idx values: @ and * return the whole array as Value::Array (which fuses correctly via the Op::Exec splice for argv splice).
BUILTIN_ARRAY_JOIN
Builtin ID for Array → String joining. Pops one value: if it’s an Array, joins its string-coerced elements with a single space; otherwise passes through. Used after Op::Glob to convert the pattern’s matched paths into the single argv-token form the bytecode word model expects (no per-word splitting yet — that’s a future phase).
BUILTIN_ARRAY_JOIN_STAR
BUILTIN_ARRAY_LENGTH
${#arr[@]} and ${#arr} (when arr is an array name) — array length. Pops one arg: name. Returns Value::str of len.
BUILTIN_BRACE_EXPAND
Brace expansion: {a,b,c} → 3 values, {1..5} → 5 values, {01..05} → zero-padded numerics, {a..e} → letter range. Pops one string, returns Value::Array of expansions (empty array → original string preserved).
BUILTIN_CMD_SUBST_TEXT
$(cmd) command substitution. Pops [cmd_string], runs through run_command_substitution which compiles via ZshParser+ZshCompiler and captures stdout via an in-process pipe. Returns trimmed output as Value::Str. Avoids the sub-chunk word-emit quoting bug in the raw Op::CmdSubst path.
BUILTIN_CONCAT_DISTRIBUTE
Word-segment concat that does cartesian-product distribution over arrays. Stack: [lhs, rhs]. Used for RC_EXPAND_PARAM ${arr} and explicit-distribute forms (${^arr}, ${(@)…}).
BUILTIN_CONCAT_SPLICE
Word-segment concat with FIRST/LAST sticking. Stack: [lhs, rhs]. Used for default unquoted splice forms (${arr[@]}, $@, $*) where prefix sticks to first element only and suffix to last only.
BUILTIN_ERREXIT_CHECK
BUILTIN_EXPAND_TEXT
Text-based word expansion. Pops [preserved_text]: the word with quotes preserved (DNULL→", SNULL→', BNULL→\), runs expand_string (variable + cmd-sub + arith) then expand_braces then expand_glob. Returns Value::str (single match) or Value::Array (multi-match brace/glob).
BUILTIN_FILE_MODIFIED_SINCE_ACCESS
[[ -N path ]] — file modified since last accessed (atime <= mtime).
BUILTIN_FILE_NEWER
[[ a -nt b ]] — file a newer than file b (mtime strict). Stack: [path_a, path_b]. Pushes Bool. zsh-compatible “missing” rules: if both exist, compare mtime; if only a exists → true; otherwise false.
BUILTIN_FILE_OLDER
[[ a -ot b ]] — mirror of -nt. If both exist, compare mtime; if only b exists → true; otherwise false.
BUILTIN_GET_VAR
Builtin ID for ${name} reads — routes through ShellExecutor::get_variable which knows about special params ($?, $@, $#, $1..$9), shell vars (self.variables), arrays, and env. Replaces emission of Op::GetVar for shell variable names so nested VMs (function calls) see the same storage.
BUILTIN_GLOB_EXPAND
Pop a scalar from the VM stack, run expand_glob on it, push the result as Value::Array. Used by the segment-concat compile path when var refs concatenate with glob meta literals ($D/*, ${prefix}*, etc.) — those skip the bridge’s pathname-expansion pass and would otherwise leak the glob meta to argv as a literal.
BUILTIN_GLOB_QUALIFIED
Glob qualifier filter: *(qualifier) filters glob results by predicate. Pops [pattern, qualifier_string]. Returns Value::Array of matching paths.
BUILTIN_HAS_SETGID
[[ -g path ]] — setgid bit (S_ISGID).
BUILTIN_HAS_SETUID
[[ -u path ]] — setuid bit (S_ISUID).
BUILTIN_HAS_STICKY
[[ -k path ]] — sticky bit (S_ISVTX) set on path.
BUILTIN_IS_BLOCKDEV
[[ -b path ]] — block device.
BUILTIN_IS_CHARDEV
[[ -c path ]] — character device.
BUILTIN_IS_FIFO
[[ -p path ]] — FIFO / named pipe.
BUILTIN_IS_SOCKET
[[ -S path ]] — socket.
BUILTIN_IS_TTY
[[ -t fd ]] — fd-is-a-tty check. Stack: [fd_string]. Routes through libc::isatty. Pushes Bool.
BUILTIN_OPEN_NAMED_FD
{name}>file / {name}<file / {name}>>file — named-fd allocation. Stack: [path, varid, op_byte]. Opens path per op_byte, gets the new fd (≥10 in zsh; we use libc::open with O_CLOEXEC bit cleared so the inherited fd survives Command::new spawns), stores the fd number as a string in $varid. Pushes Status (0 success, 1 error).
BUILTIN_OPTION_SET
[[ -o option ]] — shell-option-set test. Stack: [option_name]. Normalizes the name (strip underscores, lowercase) and reads exec.options. Pushes Bool.
BUILTIN_OWNED_BY_GROUP
[[ -G path ]] — owned by effective GID.
BUILTIN_OWNED_BY_USER
[[ -O path ]] — owned by effective UID.
BUILTIN_PARAM_DEFAULT_FAMILY
Phase 1 native param-modifier builtins. Each takes a fixed argv shape and returns the modified value as Value::Str. Replaces the runtime ShellWord round-trip via BUILTIN_EXPAND_WORD_RUNTIME for the common shapes.
BUILTIN_PARAM_FILTER
${var:#pattern} — array filter: remove elements matching pattern. Stack: [name, pattern]. For scalar var, returns empty if it matches the pattern, else the value. For array var, returns Array of non-matching elements.
BUILTIN_PARAM_FLAG
${(flags)name} — zsh parameter expansion flags. Stack: [name, flags]. Flags applied left-to-right. Supported subset (high-value, used by zpwr):
BUILTIN_PARAM_LENGTH
${#name} — character length of a scalar value, or element count of an indexed/assoc array. Pops [name], returns count as Value::Str.
BUILTIN_PARAM_REPLACE
${var/pat/repl} / ${var//pat/repl} / ${var/#pat/repl} / ${var/%pat/repl} — pop [name, pattern, replacement, op_byte]. op_byte: 0=first, 1=all, 2=anchor-prefix, 3=anchor-suffix.
BUILTIN_PARAM_STRIP
${var#pat} / ${var##pat} / ${var%pat} / ${var%%pat} — pop [name, pattern, op_byte]. op_byte: 0=#, 1=##, 2=%, 3=%%.
BUILTIN_PARAM_SUBSTRING
${var:offset[:length]} — pop [name, offset, length] (length=-1 means “rest of value”; negative offset counts from end).
BUILTIN_PARAM_SUBSTRING_EXPR
BUILTIN_REGEX_MATCH
Re-export the regex_match host method as a builtin so [[ s =~ pat ]] works even when fusevm’s Op::RegexMatch isn’t routed (compat fallback).
BUILTIN_REGISTER_COMPILED_FN
Register a pre-compiled fusevm chunk as a function. Stack: [name, base64-bincode-of-Chunk]. Used by compile_zsh’s compile_funcdef to register functions parsed via ZshParser without going through the ShellCommand JSON serialization path.
BUILTIN_RUN_BG
Builtin ID for cmd & background execution. IDs 287/288/289 are reserved for the planned array work in Phase G1 (SET_ARRAY/SET_ASSOC/ARRAY_INDEX), so this lands at 290. Pops one sub-chunk index; forks; child detaches (setsid), runs the sub-chunk on a fresh VM, exits with last_status; parent returns Status(0) immediately. Job-table registration (so jobs/fg/wait can see the pid) is deferred to Phase G6 — fire-and-forget for now.
BUILTIN_RUN_COPROC
coproc [name] { body } — bidirectional pipe to async child. Pops a name (optional, “” for default) and a sub-chunk index. Creates two pipes, forks, child redirects its fd 0/1 to the inner ends and runs the body, parent stores [write_fd, read_fd] into the named array (default COPROC). Caller closes the fds and waits when done. Job-table integration deferred to Phase G6 alongside the bg & work.
BUILTIN_RUN_PIPELINE
Builtin ID for pipeline execution. Pops N sub-chunk indices from the stack; each index points into vm.chunk.sub_chunks (compiled stage bodies). Forks N children, wires stdin/stdout between them via pipes, runs each stage’s bytecode on a fresh VM in its child, parent waits for all and pushes the last stage’s exit status. This is bytecode-native pipeline execution — no tree-walker delegation.
BUILTIN_RUN_SELECT
select var in words; do body; done — interactive numbered-menu loop. Compile emits N word pushes + var-name push + sub-chunk index push, then CallBuiltin(296, N+2). Handler prints 1) word1\n2) word2\n... to stderr, prints $PROMPT3 (default ?# ) to stderr, reads a line from stdin. On EOF returns 0. On a valid 1-based number, sets var to the chosen word, runs the sub-chunk, then redisplays the menu and loops. On invalid input redraws the menu without running the body. break from inside the body exits the loop (handled by the body’s own bytecode).
BUILTIN_SAME_FILE
[[ a -ef b ]] — same-inode test. Stack: [a, b]. Pushes Bool true iff both paths resolve to the same (dev, inode) pair (zsh + bash semantics).
BUILTIN_SET_ARRAY
Indexed-array assignment: arr=(a b c). Compile_simple emits N element pushes followed by name push, then CallBuiltin(BUILTIN_SET_ARRAY, N+1). The handler pops args (last popped = name in our pushing order) and stores Vec<String> into executor.arrays. Tree-walker callers see the same storage. Any prior scalar binding in executor.variables for name is removed so ${name} (scalar context) consistently reflects the array’s first element via get_variable.
BUILTIN_SET_ASSOC
Single-key set on an associative array: foo[key]=val. Stack (top-down): [name, key, value]. Stores value into executor.assoc_arrays[name][key], creating the outer entry if missing. compile_simple detects var[...]=... in assignments and emits this builtin.
BUILTIN_SET_BREAK
break from inside a body that runs on a sub-VM (select, future loop-via- builtin constructs). Sets executor.loop_signal = Some(LoopSignal::Break). Outer-loop builtins drain the flag after each body run and exit early.
BUILTIN_SET_CONTINUE
continue from inside a sub-VM body. Sets the signal to Continue. Outer loop builtins drain + skip-to-next-iteration.
BUILTIN_SET_LINENO
Update $LINENO to track the source line of the next statement. Stack: [n] (the line number from ZshPipe.lineno). Direct port of zsh’s lineno global tracking (Src/input.c:330) — the compiler emits one of these per top-level pipe so $LINENO reflects the source position at runtime. ID 342 picked because the previous 326 collided with BUILTIN_HAS_STICKY (the file has several other duplicate IDs — 325 has two as well — but fixing those is out of scope for this port).
BUILTIN_SET_RAW_OPT
BUILTIN_SET_SUBSCRIPT_RANGE
a[i]=(elements) / a[i,j]=(elements) / a[i]=() — subscripted-array assign with array-literal RHS. Stack: […elements, name, key]. Empty elements + single-int key a[i]=() removes that element. Comma-key a[i,j]=(...) splices.
BUILTIN_SET_TRY_BLOCK_ERROR
Capture current last_status into the TRY_BLOCK_ERROR variable. Emitted between the try block and the always block of { … } always { … } so the finally arm can read $TRY_BLOCK_ERROR.
BUILTIN_SET_VAR
Builtin ID for name=value assignments — pops [name, value] and stores into executor.variables. Replaces Op::SetVar emission for the same reason: the storage must be visible to both bytecode and tree-walker code, across nested VM boundaries.
BUILTIN_TIME_SUBLIST
time { compound; ... } — wall-clock-time the sub-chunk and print elapsed seconds. Stack: [sub_chunk_idx as Int]. Runs the sub-chunk on the current VM (so positional/local state is shared) and prints the timing summary to stderr in zsh’s format. Pushes Status.
BUILTIN_UNKNOWN_COND
[[ -X file ]] for unknown unary test op -X. Stack: [op_name]. Emits zsh’s unknown condition: -X diagnostic to stderr and pushes Bool(false). Without this, unknown conditions silently returned false matching neither zsh’s error format nor the expected status code (zsh returns 2 for parse error).
BUILTIN_VAR_EXISTS
BUILTIN_WORD_SPLIT
Word-split a string on IFS (default: whitespace). Pops one string, returns Value::Array of fields. Used in array-literal context where arr=($(cmd)) should expand cmd’s stdout into multiple elements.
BUILTIN_XTRACE_LINE

Functions§

format_function_body_zsh
Format a function body the way zsh’s typeset -f / functions display it: each top-level statement on its own line (split on ; and \n), trailing semicolons stripped, no empty lines. Matches /bin/zsh -f -c 'f() { echo a; echo b; }; typeset -f f' output: f () { (tab)echo a (tab)echo b }
format_int_in_base
Format an integer in the given base (2-36) using zsh’s BASE#DIGITS form. Bases 2-9 are unsigned-style; uppercase A-Z are used for digits
pretty_io_err
Strip Rust’s “(os error N)” suffix from an io::Error display so the message matches BSD/GNU coreutils’ output (e.g. zsh’s bundled cat/head emit cat: foo: No such file or directory, not cat: foo: No such file or directory (os error 2)). Used by all the in-process coreutils builtins.
underscore_separate_digits
Insert _ separators every group digits, counting from the end of the digit run (right-to-left). Direct port of convbase_underscore in src/zsh/Src/params.c:5645-5680. Operates on the digit suffix only, preserving any BASE# prefix and leading sign.