<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<meta name="description" content="zshrs full reference — every builtin, keyword, parameter-expansion form, ZshFlag, array shape, AOP intercept, parallel primitive, and coreutils replacement. Code examples for each.">
<title>zshrs — Reference</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="hud-static.css">
<link rel="stylesheet" href="tutorial.css">
<style>
.tutorial-main { max-width: 72rem; }
.docs-build-line {
margin: 0.35rem 0 0;
font-family: 'Share Tech Mono', ui-monospace, monospace;
font-size: 11px; color: var(--text-dim);
letter-spacing: 0.03em; max-width: 42rem; opacity: 0.75;
}
.hub-scheme-strip {
border-bottom: 1px dashed var(--border);
background: color-mix(in srgb, var(--bg-secondary) 85%, transparent);
padding: 0.55rem 1.5rem 0.65rem; position: relative;
}
.hub-scheme-strip-inner {
max-width: 72rem; margin: 0 auto;
display: flex; align-items: center; gap: 0.85rem;
}
.hub-scheme-strip .hud-scheme-label {
flex: 0 0 auto;
font-family: 'Orbitron', sans-serif; font-size: 9px; font-weight: 700;
letter-spacing: 2px; text-transform: uppercase; color: var(--accent);
}
.hub-scheme-strip .scheme-grid {
flex: 1 1 auto;
display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px;
}
@media (max-width: 720px) {
.hub-scheme-strip-inner { flex-direction: column; align-items: stretch; }
.hub-scheme-strip .scheme-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.chapter-index {
list-style: none; padding: 0; margin: 0;
display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
gap: 0.3rem;
}
.chapter-index li {
border: 1px solid var(--border); padding: 0.45rem 0.65rem; border-radius: 2px;
background: color-mix(in srgb, var(--bg-card) 92%, transparent);
display: flex; justify-content: space-between; align-items: baseline;
}
.chapter-index li a {
color: var(--cyan); text-decoration: none; font-size: 13px;
font-family: 'Share Tech Mono', ui-monospace, monospace;
}
.chapter-index li a:hover { color: var(--accent-light); }
.chapter-count {
font-size: 10px; color: var(--text-muted);
font-family: 'Share Tech Mono', ui-monospace, monospace;
}
.chapter-meta {
font-size: 11px; color: var(--text-muted); margin: -0.3rem 0 0.8rem;
font-family: 'Share Tech Mono', ui-monospace, monospace;
}
.doc-entry {
margin: 1rem 0 1.4rem;
padding: 0.75rem 0.9rem 0.5rem;
border-left: 2px solid var(--cyan);
background: color-mix(in srgb, var(--bg) 94%, transparent);
border-radius: 2px;
}
.doc-entry h3 {
margin: 0 0 0.45rem;
font-family: 'Orbitron', sans-serif;
font-size: 13px; font-weight: 700; letter-spacing: 1.5px;
text-transform: uppercase; color: var(--cyan);
}
.doc-entry h3 code {
color: var(--accent-light); background: transparent; border: none;
padding: 0; font-size: 1em; letter-spacing: 0.5px;
}
.doc-entry .doc-anchor {
color: var(--text-muted); font-size: 0.85em; margin-right: 0.25rem;
text-decoration: none;
}
.doc-entry .doc-anchor:hover { color: var(--accent); }
.doc-entry h4 {
font-family: 'Orbitron', sans-serif;
font-size: 11px; font-weight: 700; letter-spacing: 1.5px;
text-transform: uppercase; color: var(--accent-light);
margin: 0.8rem 0 0.3rem;
}
.doc-entry p {
font-size: 13px; line-height: 1.6; color: var(--text-dim);
margin: 0.35rem 0;
}
.doc-entry p code, .doc-entry li code {
color: var(--accent-light); font-size: 12px;
}
.doc-entry ul { margin: 0.3rem 0 0.5rem; padding-left: 1.25rem; }
.doc-entry li { font-size: 13px; color: var(--text-dim); line-height: 1.55; margin: 0.2rem 0; }
.doc-entry pre {
font-family: 'Share Tech Mono', ui-monospace, monospace;
font-size: 12px;
background: var(--bg); border: 1px solid var(--border);
border-radius: 2px;
padding: 0.7rem 0.9rem; overflow-x: auto;
color: var(--text); margin: 0.5rem 0;
box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.35);
}
.doc-entry pre code { color: var(--text); background: transparent; border: none; padding: 0; }
[data-theme="light"] .doc-entry pre { box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05); }
kbd {
font-family: 'Share Tech Mono', ui-monospace, monospace;
font-size: 11px;
padding: 1px 6px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-bottom-width: 2px;
border-radius: 3px;
color: var(--cyan);
}
</style>
</head>
<body>
<div class="app tutorial-app" id="docsApp">
<div class="crt-scanline" id="crtH" aria-hidden="true"></div>
<div class="crt-scanline-v" id="crtV" aria-hidden="true"></div>
<header class="tutorial-header">
<div class="tutorial-header-inner">
<div>
<h1 class="tutorial-brand">// ZSHRS — FULL REFERENCE</h1>
<nav class="tutorial-crumbs" aria-label="Breadcrumb">
<a href="index.html">Docs</a>
<span class="sep">/</span>
<span class="current">Reference</span>
<span class="sep">/</span>
<a href="https://github.com/MenkeTechnologies/zshrs" target="_blank" rel="noopener noreferrer">GitHub</a>
</nav>
<p class="docs-build-line">zshrs v0.10.9 · 15 chapters · every supported builtin, keyword, expansion form & AOP primitive — pinned by 565 regression tests (392 corpus · 160 dispatch · 8 absence · 5 unit) · 4,849 <code>#[test]</code> hooks workspace-wide</p>
</div>
<div class="tutorial-toolbar">
<button type="button" class="btn btn-secondary" id="btnTheme" title="Toggle light/dark">Theme</button>
<button type="button" class="btn btn-secondary active" id="btnCrt" title="CRT scanline overlay">CRT</button>
<button type="button" class="btn btn-secondary active" id="btnNeon" title="Neon border pulse">Neon</button>
<a class="btn btn-secondary" href="index.html">Hub</a>
<a class="btn btn-secondary" href="https://github.com/MenkeTechnologies/zshrs" target="_blank" rel="noopener noreferrer">GitHub</a>
</div>
</div>
</header>
<div class="hub-scheme-strip">
<div class="hub-scheme-strip-inner">
<span class="hud-scheme-label">// Color scheme</span>
<div class="scheme-grid" id="hudSchemeGrid"></div>
</div>
</div>
<main class="tutorial-main">
<h2 class="tutorial-title"><span class="step-hash">>_</span>SHELL REFERENCE</h2>
<p class="tutorial-subtitle">Every builtin, keyword, parameter-expansion form, ZshFlag, array shape, AOP primitive, parallel primitive, and anti-fork coreutils replacement that zshrs ships. Each entry shows what it does and a runnable example. <strong>Coverage gate:</strong> 392 construct-corpus tests in <code>tests/zsh_construct_corpus.rs</code> exercise every category below; <strong>4,849 <code>#[test]</code> hooks</strong> (<code>rg '^\s*#\[test\]'</code> across <code>src/</code>, <code>tests/</code>, <code>compsys/</code>, <code>daemon/</code>, <code>fish/</code>, <code>bins/</code>, <code>build.rs</code>, 2026-05-12) span the workspace. Jump via the chapter index, or <kbd>Ctrl+F</kbd> for a specific name.</p>
<section class="tutorial-section">
<h2>Pipeline Architecture</h2>
<p>Two execution pipelines exist; the new one is the default as of session 2026-04-27:</p>
<ul>
<li><strong>New (default):</strong> <code>crate::ported::lex</code> (port of <code>src/zsh/Src/lex.c</code>, in <code>src/ported/lex.rs</code>) → <code>crate::ported::parse</code> (port of <code>src/zsh/Src/parse.c</code>, in <code>src/ported/parse.rs</code>) → <code>ZshCompiler</code> (original Rust, <code>src/extensions/compile_zsh.rs</code>) → fusevm bytecode. Lex + parse are strict-port code; the bytecode compiler lives in <code>src/extensions/</code> because it has no C ancestor. Faster + more faithful to zsh's grammar than the legacy tree-walker.</li>
<li><strong>Old (escape hatch):</strong> set <code>ZSHRS_OLD_PIPELINE=1</code> to route through the legacy hand-rolled <code>ShellLexer</code>/<code>ShellParser</code>/<code>ShellCompiler</code>. Slated for deletion once corpus parity is proven across all dogfood scenarios.</li>
</ul>
<p style="font-size: 12px; color: var(--text-muted);">A residual bridge inside <code>compile_word_str</code> still re-parses param-modifier forms (<code>${x:-default}</code>, <code>${x#prefix}</code>, <code>${x/pat/rep}</code>) through the legacy <code>ShellParser</code> via <code>untokenize_preserve_quotes</code> round-trip. Each iteration of the migration replaces one runtime-fallback call site with native ops; tracked as Phase 1 / G8 in <code>ROADMAP.md</code>.</p>
</section>
<section class="tutorial-section">
<h2>Coverage Gate</h2>
<p>Each construct in this reference is pinned by at least one assertion in
<code>tests/zsh_construct_corpus.rs</code>. The corpus runs the real
<code>zshrs -f -c <script></code> binary and asserts exact stdout +
exit status. Categories covered:</p>
<ul>
<li>Simple commands, multi-word arguments, assignments (chained, empty, quoted, single-quoted)</li>
<li>List operators: <code>;</code>, <code>&&</code>, <code>||</code>, <code>&</code>, short-circuit chains</li>
<li>Control flow: <code>if</code>/<code>elif</code>/<code>else</code>, <code>while</code>, <code>until</code>, <code>for in</code>, <code>for ((;;))</code>, <code>case</code> (literal/glob/alternation), <code>select</code>, <code>coproc</code></li>
<li><code>break</code> / <code>continue</code> / <code>return</code> with status</li>
<li>Functions: <code>name() {}</code>, <code>function</code> keyword, <code>local</code> scope, <code>$1…$#</code> + <code>"$@"</code> splice</li>
<li>Pipelines: 2/3-stage, function-in-first-stage, negation</li>
<li>Redirects: <code>></code>, <code>>></code>, <code><</code>, <code><<EOF</code>, <code><<<str</code>, <code>2>&1</code>, block redir, <code><&fd</code> with var-expanded fd, close-fd</li>
<li>Quoting: single, double, <code>$'…'</code> ANSI-C, backtick, escaped <code>\$</code> in dq</li>
<li>Parameter expansion: every modifier from zshexpn(1) — default, assign, error, alternate, length, substring, strip prefix/suffix (short/long, with glob), replace first/all, upper/lower postfix</li>
<li>ZshFlags: L, U, j:s:, s:s:, f, o, O, P, @, k, v, #, q (level 1-4), q+, q-, q*, q!, g, n, i, t, %, e, p, A, ~, plus stacking</li>
<li>Special parameters: <code>$?</code>, <code>$$</code>, <code>$@</code>, <code>$*</code>, <code>$#</code>, <code>$N</code>, <code>$!</code>, <code>$_</code>, <code>$-</code></li>
<li>Command substitution: <code>$(…)</code>, backticks, nested, in array literal (with word-split)</li>
<li>Arithmetic: <code>$((…))</code>, <code>((…))</code>, <code>let</code> — addition/precedence/paren/div/mod/pow/bitwise/shift/inc/dec/compare/logical/ternary, with var sync to <code>executor.variables</code></li>
<li>Tilde, brace ranges (numeric/letter/zero-pad), brace alternation (nested + prefix/suffix), brace in for-loop</li>
<li>Glob: <code>*</code>, <code>?</code>, <code>[…]</code>, qualifiers (<code>(.)</code>, <code>(/)</code>, <code>(@)</code>, <code>(N)</code>, <code>(x/r/w)</code>, sort modifiers <code>o</code>/<code>O</code>/<code>oL</code>/<code>OL</code>/<code>om</code>/<code>Om</code>)</li>
<li>Process substitution: <code><(cmd)</code>, <code>>(cmd)</code></li>
<li>Heredoc + herestring</li>
<li>Indexed arrays: literal, append <code>+=()</code>, index 1-based + negative, splice <code>${arr[@]}</code>, length, iteration, empty, quoted-element preservation</li>
<li>Associative arrays: <code>typeset -A</code>, <code>m[k]=v</code>, <code>m[k]+=tail</code>, lookup, keys, values, missing-key empty</li>
<li><code>[[ … ]]</code>: <code>==</code>/<code>!=</code>/<code><</code>/<code>></code>, glob, <code>-z</code>/<code>-n</code>, numeric <code>-eq</code>/<code>-lt</code>/<code>-le</code>/<code>-gt</code>/<code>-ge</code>, file tests <code>-e</code>/<code>-f</code>/<code>-d</code>/<code>-r</code>, logical <code>&&</code>/<code>||</code>/<code>!</code>, regex <code>=~</code> with anchors + capture groups</li>
<li>POSIX builtins: <code>cd</code>, <code>pwd</code>, <code>echo</code>, <code>print</code>, <code>printf</code>, <code>read</code> (single/multi-var/array), <code>eval</code>, <code>exec</code>, <code>set</code>, <code>shift</code>, <code>unset</code>, <code>export</code>, <code>alias</code>/<code>unalias</code>, <code>type</code>, <code>true</code>/<code>false</code>/<code>:</code>, <code>test</code>/<code>[</code></li>
<li>Coreutils builtins (anti-fork): <code>seq</code>, <code>basename</code>, <code>dirname</code>, <code>tr</code> (with range expansion), <code>cat</code>, <code>head</code>/<code>tail</code>, <code>wc</code>, <code>sort</code>, <code>uniq</code>, <code>find</code>, <code>cut</code>, <code>rev</code>, <code>tee</code>, <code>sleep</code>, <code>mktemp</code>, <code>whoami</code>, <code>id</code>, <code>hostname</code>, <code>uname</code>, <code>date</code>, <code>touch</code>, <code>realpath</code></li>
<li>Background <code>&</code>, <code>async</code>/<code>await</code>, coproc round-trip via <code>/dev/fd/N</code></li>
<li>Eval with deferred expansion (single-quoted args), multi-command eval</li>
<li>Bang (<code>!</code>) — literal in non-interactive (with surrounding chars), command negation when standalone</li>
<li>Aliases — regular + with args</li>
<li>Subshells <code>(…)</code> with full state isolation (variables/arrays/assoc/positional/<strong>cwd</strong> restored)</li>
<li><code>(exit N)</code> in a subshell exits the subshell only, not the parent; status propagates through <code>SubshellEnd</code></li>
<li>Brace groups <code>{…}</code> (no isolation, command-scope only) — and <em>compound forms with trailing redirects</em>: <code>{ … } 2>&1</code>, <code>(…) >file</code>, <code>if …; fi >file</code> via <code>ZshCommand::Redirected</code></li>
<li>Anonymous functions <code>() { body } args…</code> — auto-call form executes once with the trailing words as <code>$1…$N</code></li>
<li>Trap EXIT (fires on script exit through both pipelines)</li>
<li>Repeat N cmd</li>
<li>Case fall-through <code>;&</code> (run next arm without re-testing) and test-next <code>;|</code> (re-test next arm's pattern)</li>
<li><code>|&</code> pipe with stderr merge (<code>cmd1 |& cmd2</code> ≡ <code>cmd1 2>&1 | cmd2</code>)</li>
<li>Heredoc variants: <code><<EOF</code> (var-expand), <code><<-EOF</code> (strip leading tabs), <code><<'EOF'</code> / <code><<"EOF"</code> (quoted-terminator → verbatim, no expansion)</li>
<li>Special parameters: <code>$?</code>, <code>$$</code>, <code>$@</code>, <code>$*</code>, <code>$#</code>, <code>$N</code>, <code>$!</code>, <code>$_</code>, <code>$-</code>, <code>$RANDOM</code>, <code>$SECONDS</code>, <code>$EPOCHSECONDS</code>, <code>$LINENO</code>, <code>$REPLY</code>, <code>PIPESTATUS</code> / <code>pipestatus</code></li>
<li><code>[[ -v var ]]</code> existence test (scalar/array/assoc/env)</li>
<li><code>${a[$i]}</code> variable subscript (<code>$i</code>, <code>${expr}</code>, <code>$((arith))</code> all resolve before subscript parsing)</li>
<li><code>${v: -3}</code> negative substring offset (space disambiguates from <code>${v:-default}</code>)</li>
<li><code>${var:=default}</code>, <code>${var:?msg}</code>, <code>${var:+alt}</code>, <code>${v/#prefix/repl}</code> / <code>${v/%suffix/repl}</code> (anchor variants of replace)</li>
<li>Arithmetic with <code>$NAME</code> / <code>${NAME}</code> / <code>$N</code> inside <code>((…))</code>, <code>$((…))</code>, <code>for ((…))</code> — pre-loaded via <code>BUILTIN_GET_VAR</code></li>
<li><code>set -e</code>, <code>getopts</code>, <code>read</code> into multiple vars / default <code>$REPLY</code></li>
<li><code>printf</code> format-string repetition (POSIX: re-applies format while args remain)</li>
</ul>
<p style="margin-top: 0.7rem; font-size: 12px; color: var(--text-muted);">
Out-of-scope (separate Phase work): zsh modules (zsh/curses, zsh/net/socket, zsh/zftp, zsh/zselect, zsh/sched), interactive ZLE widget firing on Tab + bindkey'd user widgets, async <code>compinit</code> fpath pre-warm, full setopt option matrix (~200 options × 2 toggles), some long-tail glob qualifiers (<code>L+N</code> size, <code>mh-N</code> mtime, <code>om[1,N]</code> "newest N"), and the dogfood gate (zpwr load + .zshrc as login shell for 14 days). The <code>tests/ztst_runner.rs</code> wrapper has been removed from CI — it was a fake-pass per-block runner; replacement is the PTY harness designed in <code>docs/ZTST_PTY_HARNESS.md</code>, not yet built.</p>
</section>
<section class="tutorial-section">
<h2>Chapters</h2>
<ul class="chapter-index">
<li><a href="#ch-control-flow">Control Flow</a> <span class="chapter-count">14</span></li>
<li><a href="#ch-arrays-hashes">Arrays & Associative Arrays</a> <span class="chapter-count">14</span></li>
<li><a href="#ch-param-expansion">Parameter Expansion</a> <span class="chapter-count">21</span></li>
<li><a href="#ch-zsh-flags">Zsh Parameter Flags</a> <span class="chapter-count">21</span></li>
<li><a href="#ch-redirects-pipes">Redirection & Pipelines</a> <span class="chapter-count">15</span></li>
<li><a href="#ch-shell-builtins">POSIX / Zsh Builtins</a> <span class="chapter-count">52</span></li>
<li><a href="#ch-job-control">Job Control & Background</a> <span class="chapter-count">11</span></li>
<li><a href="#ch-coreutils">Anti-Fork Coreutils</a> <span class="chapter-count">23</span></li>
<li><a href="#ch-parallel">Parallel Primitives</a> <span class="chapter-count">5</span></li>
<li><a href="#ch-aop">AOP Intercept</a> <span class="chapter-count">3</span></li>
<li><a href="#ch-diagnostics">Diagnostics & Profiling</a> <span class="chapter-count">5</span></li>
<li><a href="#ch-completion">Completion System</a> <span class="chapter-count">10</span></li>
<li><a href="#ch-zle">ZLE Line Editor</a> <span class="chapter-count">4</span></li>
<li><a href="#ch-pcre">PCRE & Regex</a> <span class="chapter-count">4</span></li>
<li><a href="#ch-recently-closed">Recently Closed Compat Gaps</a> <span class="chapter-count">46</span></li>
</ul>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-control-flow">
<h2>Control Flow</h2>
<p class="chapter-meta">14 topics · compiled to fusevm bytecode with deferred jump patches; no tree-walker dispatch. Includes anonymous-fn auto-call and <code>case</code> fall-through (<code>;&</code>) / test-next (<code>;|</code>).</p>
<article class="doc-entry" id="doc-if">
<h3><a class="doc-anchor" href="#doc-if">#</a> <code>if … then … fi</code></h3>
<p>Standard POSIX conditional. <code>elif</code> branches and an optional <code>else</code> are supported. Status semantics: a Status(0) is truthy in zshrs, so <code>if cmd; then …; fi</code> runs the <em>then</em> arm when <code>cmd</code> exits 0. The compile path emits <code>JumpIfFalse</code> at the end of each conditional probe.</p>
<pre><code>if [[ -d "$dir" ]]; then
cd "$dir"
elif [[ -f "$dir" ]]; then
echo "regular file"
else
echo "no such path"
fi</code></pre>
</article>
<article class="doc-entry" id="doc-while">
<h3><a class="doc-anchor" href="#doc-while">#</a> <code>while … do … done</code></h3>
<p>Loops while the condition exits 0. Loop-body status is preserved across iterations via a dedicated slot — the loop's exit status is the body's last-command status (or 0 if the body never ran), matching POSIX.</p>
<pre><code>i=0
while (( i < 5 )); do
echo "iter $i"
(( i++ ))
done</code></pre>
</article>
<article class="doc-entry" id="doc-until">
<h3><a class="doc-anchor" href="#doc-until">#</a> <code>until … do … done</code></h3>
<p>Inverse of <code>while</code> — loops while the condition is non-zero. Compiles to the same bytecode shape with the condition jump inverted.</p>
<pre><code>until ping -c1 -W1 example.com >/dev/null; do
sleep 1
done
echo "online"</code></pre>
</article>
<article class="doc-entry" id="doc-for">
<h3><a class="doc-anchor" href="#doc-for">#</a> <code>for var in words; do … done</code></h3>
<p>Iterates <code>var</code> over the word list. Words are <em>compiled at runtime</em> — <code>${arr[@]}</code> splices into N iterations via <code>BUILTIN_ARRAY_FLATTEN</code>. The for-loop preserves break/continue patches as deferred jump sites so nested loops work correctly.</p>
<pre><code>for f in *.rs; do
echo "compiling $f"
done
arr=(alpha beta gamma)
for x in ${arr[@]}; do
echo "got $x"
done</code></pre>
</article>
<article class="doc-entry" id="doc-for-arith">
<h3><a class="doc-anchor" href="#doc-for-arith">#</a> <code>for ((init; cond; step)); do … done</code></h3>
<p>C-style arithmetic for loop. The arithmetic expressions compile through the inline arithmetic compiler (no <code>$((…))</code> wrapping needed inside the parens).</p>
<pre><code>for ((i = 0; i < 10; i++)); do
echo "i=$i"
done</code></pre>
</article>
<article class="doc-entry" id="doc-case">
<h3><a class="doc-anchor" href="#doc-case">#</a> <code>case … esac</code></h3>
<p>Pattern-matched dispatch. Patterns are compiled via <code>compile_case_pattern</code> which preserves <code>$</code>-expansion but does <em>not</em> glob-expand the pattern itself (otherwise <code>case x in *) …</code> would expand <code>*</code> to the cwd listing). Match check uses <code>Op::StrMatch</code> (host-routed glob match).</p>
<pre><code>case "$1" in
start) echo starting ;;
stop) echo stopping ;;
*) echo "usage: $0 {start|stop}"; exit 1 ;;
esac</code></pre>
</article>
<article class="doc-entry" id="doc-select">
<h3><a class="doc-anchor" href="#doc-select">#</a> <code>select var in words; do … done</code></h3>
<p>Interactive numbered-menu loop. Prints <code>1) word1\n2) word2\n…</code> to stderr, prompts via <code>$PROMPT3</code> (default <code>?# </code>), reads stdin, sets <code>var</code> to the chosen word, runs the body. <code>$REPLY</code> contains the raw input. EOF on stdin exits the loop. The real <code>break</code> keyword exits early via the cross-VM <code>LoopSignal</code> mechanism; the legacy <code>BREAK_SELECT=1</code> sentinel still works for back-compat.</p>
<pre><code>select choice in build test deploy quit; do
case $choice in
build) cargo build ;;
test) cargo test ;;
deploy) ./deploy.sh ;;
quit) break ;;
esac
done</code></pre>
</article>
<article class="doc-entry" id="doc-anon-fn">
<h3><a class="doc-anchor" href="#doc-anon-fn">#</a> <code>() { body } args…</code></h3>
<p>Anonymous function. Defined and immediately invoked with the trailing words as <code>$1…$N</code>. Parser routes <code>Inoutpar</code> (the <code>()</code> token) to <code>parse_anon_funcdef</code>, which generates a unique <code>_zshrs_anon_N</code> name; <code>compile_funcdef</code> registers and calls when <code>auto_call_args</code> is set. <code>()</code> with no body falls back to an empty subshell (POSIX no-op).</p>
<pre><code>() { echo "hi $1, $2 args"; } world 1
# → hi world, 1 args</code></pre>
</article>
<article class="doc-entry" id="doc-case-fall">
<h3><a class="doc-anchor" href="#doc-case-fall">#</a> <code>case;&</code> fall-through, <code>;|</code> test-next</h3>
<p>Per-arm separators after a case body. <code>;;</code> exits (default), <code>;&</code> falls through to the next arm <em>without</em> re-testing its pattern, <code>;|</code> falls through and <em>does</em> re-test. <code>compile_case</code> tracks a <code>pending_fall</code> patch site so <code>;&</code> emits a forward jump that the next arm's body-start patches.</p>
<pre><code>case "$x" in
a) print A ;& # falls into b's body
b) print B ;;
*) print other ;;
esac</code></pre>
</article>
<article class="doc-entry" id="doc-coproc">
<h3><a class="doc-anchor" href="#doc-coproc">#</a> <code>coproc [name] { body }</code></h3>
<p>Forks the body with bidirectional pipes. Two pipes are created (parent→child stdin, child→parent stdout); child <code>setsid</code>s and runs the body, parent stores <code>[read_fd, write_fd]</code> in <code>$COPROC</code> (or the given name as an array). Read child output via <code>/dev/fd/${COPROC[1]}</code>, write to its stdin via <code>/dev/fd/${COPROC[2]}</code>. Job-table integration is Phase G6.</p>
<pre><code>coproc { while read line; do echo "ECHO: $line"; done }
echo hello > /dev/fd/${COPROC[2]}
read reply < /dev/fd/${COPROC[1]}
echo "$reply" # ECHO: hello</code></pre>
</article>
<article class="doc-entry" id="doc-break">
<h3><a class="doc-anchor" href="#doc-break">#</a> <code>break</code></h3>
<p>Exits the innermost <code>for</code>/<code>while</code>/<code>until</code>. Compiled as a deferred forward <code>Jump(0)</code> patched to land just past the loop's exit point. Inside <code>select</code> use <code>BREAK_SELECT=1</code> instead until cross-construct loop control lands in Phase G6.</p>
<pre><code>for f in *.log; do
[[ -s $f ]] || break # stop on first empty
process "$f"
done</code></pre>
</article>
<article class="doc-entry" id="doc-continue">
<h3><a class="doc-anchor" href="#doc-continue">#</a> <code>continue</code></h3>
<p>Skip to the next iteration. Same patch mechanism as <code>break</code> — patches into the loop's <em>continue target</em> which is the iteration step (e.g. <code>PreIncSlotVoid</code> in the for-loop).</p>
<pre><code>for n in 1 2 3 4 5; do
(( n % 2 == 0 )) || continue
echo "even: $n"
done</code></pre>
</article>
<article class="doc-entry" id="doc-return">
<h3><a class="doc-anchor" href="#doc-return">#</a> <code>return [n]</code></h3>
<p>Return from a function with the given status (default last-status). Compile emits a forward <code>Op::Jump(0)</code> patched to past the chunk's end; standalone-chunk VMs interpret this as halt. <code>Op::Return</code> alone restarts the body from <code>ip=0</code>, which is wrong for top-level scripts.</p>
<pre><code>greet() {
[[ -z "$1" ]] && return 1
echo "hello, $1"
}</code></pre>
</article>
<article class="doc-entry" id="doc-list-ops">
<h3><a class="doc-anchor" href="#doc-list-ops">#</a> <code>;</code> <code>&&</code> <code>||</code> <code>&</code></h3>
<p>Sequential, conditional, and background separators. <code>cmd1 && cmd2</code> runs <code>cmd2</code> only if <code>cmd1</code> succeeded; <code>||</code> is the inverse. <code>&</code> backgrounds the preceding command via <code>BUILTIN_RUN_BG</code> — fork + setsid, parent returns Status(0) immediately. Compiled with deferred short-circuit jumps to avoid double-compilation of the next command.</p>
<pre><code>git pull && cargo test || echo "test failed"
sleep 30 &
echo "going to bg"</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-arrays-hashes">
<h2>Arrays & Associative Arrays</h2>
<p class="chapter-meta">13 topics · indexed arrays in <code>executor.arrays</code>, assoc in <code>executor.assoc_arrays</code>; argv splice via fusevm 0.12.1+.</p>
<article class="doc-entry" id="doc-arr-literal">
<h3><a class="doc-anchor" href="#doc-arr-literal">#</a> <code>arr=(a b c)</code></h3>
<p>Indexed-array literal assignment. Compile emits N element pushes + name push, then <code>BUILTIN_SET_ARRAY</code> (id 287) which writes a <code>Vec<String></code> into <code>executor.arrays</code> and clears any prior scalar binding for the same name.</p>
<pre><code>arr=(alpha beta "two words" gamma)
echo ${#arr[@]} # 4</code></pre>
</article>
<article class="doc-entry" id="doc-arr-append">
<h3><a class="doc-anchor" href="#doc-arr-append">#</a> <code>arr+=(d e)</code></h3>
<p>Append elements to an existing array (or create if missing). Routes through <code>BUILTIN_APPEND_ARRAY</code> (id 295) which extends <code>executor.arrays[name]</code>.</p>
<pre><code>arr=(a b)
arr+=(c d)
echo ${arr[@]} # a b c d</code></pre>
</article>
<article class="doc-entry" id="doc-arr-index">
<h3><a class="doc-anchor" href="#doc-arr-index">#</a> <code>${arr[N]}</code></h3>
<p>Indexed access. zsh is 1-based for positive indices; negative indices count from the end (<code>${arr[-1]}</code> = last). Routes through <code>BUILTIN_ARRAY_INDEX</code> (id 289). For assoc arrays the same form does string-key lookup.</p>
<pre><code>arr=(alpha beta gamma)
echo ${arr[1]} # alpha
echo ${arr[-1]} # gamma</code></pre>
</article>
<article class="doc-entry" id="doc-arr-splice-all">
<h3><a class="doc-anchor" href="#doc-arr-splice-all">#</a> <code>${arr[@]}</code></h3>
<p>Splice all elements as separate argv slots. Pushes a <code>Value::Array</code> which fusevm's <code>Op::Exec</code>/<code>Op::ExecBg</code>/<code>Op::CallFunction</code> and <code>pop_args</code> flatten into individual args. Without the splice, the array would collapse to one space-joined scalar.</p>
<pre><code>arr=(--verbose --color=always /etc /var)
ls "${arr[@]}" # ls --verbose --color=always /etc /var</code></pre>
</article>
<article class="doc-entry" id="doc-arr-length">
<h3><a class="doc-anchor" href="#doc-arr-length">#</a> <code>${#arr[@]}</code></h3>
<p>Number of elements. Routes through <code>BUILTIN_ARRAY_LENGTH</code> (id 291).</p>
<pre><code>arr=(a b c d)
echo ${#arr[@]} # 4</code></pre>
</article>
<article class="doc-entry" id="doc-arr-iterate">
<h3><a class="doc-anchor" href="#doc-arr-iterate">#</a> <code>for x in ${arr[@]}</code></h3>
<p>Iterate over array elements. The for-loop word-list compile path calls <code>BUILTIN_ARRAY_FLATTEN</code> (id 293) which flattens nested arrays one level — so <code>for x in start ${arr[@]} end</code> produces N+2 iterations, not 3.</p>
<pre><code>arr=(red green blue)
for c in ${arr[@]}; do
echo "color: $c"
done</code></pre>
</article>
<article class="doc-entry" id="doc-arr-empty">
<h3><a class="doc-anchor" href="#doc-arr-empty">#</a> <code>arr=()</code></h3>
<p>Empty-array literal. <code>${#arr[@]}</code> returns 0; iteration runs zero times.</p>
<pre><code>arr=()
for x in ${arr[@]}; do echo "never"; done
echo "len=${#arr[@]}" # len=0</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-decl">
<h3><a class="doc-anchor" href="#doc-assoc-decl">#</a> <code>typeset -A name</code> / <code>declare -A name</code></h3>
<p>Declare an associative array. The executor's <code>builtin_typeset</code> handles <code>-A</code> and pre-creates the <code>HashMap<String, String></code> entry in <code>executor.assoc_arrays</code>. Optional — <code>name[key]=val</code> creates the assoc on demand too.</p>
<pre><code>typeset -A user
user[name]=Jacob
user[role]=eng
echo "${user[name]}" # Jacob</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-set">
<h3><a class="doc-anchor" href="#doc-assoc-set">#</a> <code>name[key]=value</code></h3>
<p>Assoc-array key set. Compile detects <code>name[key]</code> shape on the LHS of an assignment and routes to <code>BUILTIN_SET_ASSOC</code> (id 288) which stores into <code>executor.assoc_arrays[name][key]</code>.</p>
<pre><code>db[host]=localhost
db[port]=5432
db[user]=admin
echo "${db[host]}:${db[port]}"</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-get">
<h3><a class="doc-anchor" href="#doc-assoc-get">#</a> <code>${name[key]}</code></h3>
<p>Assoc-array lookup. <code>BUILTIN_ARRAY_INDEX</code> checks <code>assoc_arrays</code> first; if the name has an assoc binding the string key is used directly. Missing keys return empty string.</p>
<pre><code>typeset -A m
m[a]=1; m[b]=2
echo "a=${m[a]} b=${m[b]}"
echo "missing=[${m[nope]}]" # missing=[]</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-keys">
<h3><a class="doc-anchor" href="#doc-assoc-keys">#</a> <code>${(k)name}</code></h3>
<p>List the keys of an associative array. Order is HashMap iteration order (implementation-defined). See the <a href="#ch-zsh-flags">Zsh Parameter Flags</a> chapter for the full flag set.</p>
<pre><code>typeset -A m
m[apple]=1; m[banana]=2
for k in "${(k)m}"; do echo $k; done | sort</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-values">
<h3><a class="doc-anchor" href="#doc-assoc-values">#</a> <code>${(v)name}</code></h3>
<p>List the values of an associative array. <code>${name[@]}</code> on an assoc returns the same value list.</p>
<pre><code>typeset -A m
m[apple]=1; m[banana]=2
for v in "${(v)m}"; do echo $v; done | sort</code></pre>
</article>
<article class="doc-entry" id="doc-arr-set-assoc-overwrite">
<h3><a class="doc-anchor" href="#doc-arr-set-assoc-overwrite">#</a> <code>m[k]=new</code> overwrites</h3>
<p>Re-assigning a key replaces the prior value. The set-builtin always inserts; use <code>+=</code> for append-on-existing-key semantics.</p>
<pre><code>m[k]=first
m[k]=second
echo "${m[k]}" # second</code></pre>
</article>
<article class="doc-entry" id="doc-assoc-append">
<h3><a class="doc-anchor" href="#doc-assoc-append">#</a> <code>m[k]+=tail</code></h3>
<p>Append onto an existing assoc value (string concat). If the key is missing, behaves like a plain set. Routes through <code>BUILTIN_APPEND_ASSOC</code> (id 298).</p>
<pre><code>m[host]=localhost
m[host]+=":5432"
echo "${m[host]}" # localhost:5432</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-param-expansion">
<h2>Parameter Expansion</h2>
<p class="chapter-meta">21 topics · 15 of 19 VarModifier variants lower to native fusevm ops via <code>Op::ExpandParam</code>; <code>${var:-default}</code> + <code>${#var}</code> have been native-lowered in <code>compile_zsh.rs</code> (Phase 1 steps 1–2); 4 remaining variants + stacked ZshFlags + <code>$(cmd)</code>/<code>$((expr))</code>/concat <code>${a}${b}</code> still hit the runtime fallback or the <code>untokenize_preserve_quotes</code> bridge to the legacy <code>ShellParser</code>.</p>
<article class="doc-entry" id="doc-pe-default">
<h3><a class="doc-anchor" href="#doc-pe-default">#</a> <code>${var:-default}</code></h3>
<p>Use <code>default</code> if <code>var</code> is unset or empty. Lowers to <code>Op::ExpandParam(DEFAULT)</code>. The default expression is itself compiled (variables, command substitution, etc. all expand at use time).</p>
<pre><code>name=${1:-anonymous}
log_level=${LOG_LEVEL:-info}
target=${1:-$(date +%Y%m%d)}</code></pre>
</article>
<article class="doc-entry" id="doc-pe-assign">
<h3><a class="doc-anchor" href="#doc-pe-assign">#</a> <code>${var:=default}</code></h3>
<p>Assign <code>default</code> to <code>var</code> if unset/empty, then expand. Lowers to <code>Op::ExpandParam(ASSIGN)</code>. The variable is set in <code>executor.variables</code> as a side effect.</p>
<pre><code>: ${CACHE_DIR:=$HOME/.cache}
echo "$CACHE_DIR" # CACHE_DIR is now set</code></pre>
</article>
<article class="doc-entry" id="doc-pe-error">
<h3><a class="doc-anchor" href="#doc-pe-error">#</a> <code>${var:?msg}</code></h3>
<p>Print error and exit if <code>var</code> is unset/empty. Lowers to <code>Op::ExpandParam(ERROR)</code>. Common in script preamble for required env vars.</p>
<pre><code>: ${API_TOKEN:?must be set}
: ${DEPLOY_TARGET:?usage: deploy <target>}</code></pre>
</article>
<article class="doc-entry" id="doc-pe-alternate">
<h3><a class="doc-anchor" href="#doc-pe-alternate">#</a> <code>${var:+alt}</code></h3>
<p>Expand to <code>alt</code> if <code>var</code> is set and non-empty, else empty. Lowers to <code>Op::ExpandParam(ALTERNATE)</code>.</p>
<pre><code>flag=${VERBOSE:+--verbose}
cargo build $flag</code></pre>
</article>
<article class="doc-entry" id="doc-pe-length">
<h3><a class="doc-anchor" href="#doc-pe-length">#</a> <code>${#var}</code></h3>
<p>String length of scalar (or via <code>${(#)arr}</code> the element count of an array — see Zsh Flags). Lowers to <code>Op::ExpandParam(LENGTH)</code>.</p>
<pre><code>name=Jacob
echo ${#name} # 5</code></pre>
</article>
<article class="doc-entry" id="doc-pe-substring">
<h3><a class="doc-anchor" href="#doc-pe-substring">#</a> <code>${var:offset:length}</code></h3>
<p>Substring extraction. Lowers to <code>Op::ExpandParam(SLICE)</code>. Negative offsets count from the end. Length is optional (omitted = to end).</p>
<pre><code>s=abcdefgh
echo ${s:2:3} # cde
echo ${s:-3} # fgh (last 3 chars)
echo ${s:2} # cdefgh (offset 2 to end)</code></pre>
</article>
<article class="doc-entry" id="doc-pe-strip-prefix">
<h3><a class="doc-anchor" href="#doc-pe-strip-prefix">#</a> <code>${var#pat}</code> / <code>${var##pat}</code></h3>
<p>Strip shortest / longest matching prefix. <code>#</code> = shortest, <code>##</code> = longest. Lowers to <code>STRIP_SHORT</code> / <code>STRIP_LONG</code>.</p>
<pre><code>path=/usr/local/bin/zshrs
echo ${path##*/} # zshrs (basename via longest-prefix-strip)
echo ${path#*/} # usr/local/bin/zshrs</code></pre>
</article>
<article class="doc-entry" id="doc-pe-strip-suffix">
<h3><a class="doc-anchor" href="#doc-pe-strip-suffix">#</a> <code>${var%pat}</code> / <code>${var%%pat}</code></h3>
<p>Strip shortest / longest matching suffix. <code>%</code> = shortest, <code>%%</code> = longest. Lowers to <code>RSTRIP_SHORT</code> / <code>RSTRIP_LONG</code>.</p>
<pre><code>file=archive.tar.gz
echo ${file%.gz} # archive.tar
echo ${file%%.*} # archive (dirname via longest-suffix-strip)</code></pre>
</article>
<article class="doc-entry" id="doc-pe-replace-first">
<h3><a class="doc-anchor" href="#doc-pe-replace-first">#</a> <code>${var/pat/repl}</code></h3>
<p>Replace first match of <code>pat</code> with <code>repl</code>. Lowers to <code>SUBST_FIRST</code>. <code>repl</code> may use <code>&</code> for the matched text in some shells; zshrs follows zsh's literal-replace semantics.</p>
<pre><code>s="foo bar foo"
echo ${s/foo/baz} # baz bar foo</code></pre>
</article>
<article class="doc-entry" id="doc-pe-replace-all">
<h3><a class="doc-anchor" href="#doc-pe-replace-all">#</a> <code>${var//pat/repl}</code></h3>
<p>Replace <em>all</em> matches. Lowers to <code>SUBST_ALL</code>. Heavily used by zpwr-style idioms (path normalization, sigil replacement).</p>
<pre><code>s="hello world hello"
echo ${s//hello/HI} # HI world HI
path="/a/b/c"
echo ${path//\//.} # .a.b.c</code></pre>
</article>
<article class="doc-entry" id="doc-pe-upper">
<h3><a class="doc-anchor" href="#doc-pe-upper">#</a> <code>${var:u}</code> / <code>${(U)var}</code></h3>
<p>Uppercase. The <code>:u</code> postfix lowers to <code>Op::ExpandParam(UPPER)</code>; the <code>(U)</code> flag form goes through <code>BUILTIN_PARAM_FLAG</code> and supports stacking with other flags.</p>
<pre><code>x=hello
echo ${x:u} # HELLO
echo ${(U)x} # HELLO</code></pre>
</article>
<article class="doc-entry" id="doc-pe-lower">
<h3><a class="doc-anchor" href="#doc-pe-lower">#</a> <code>${var:l}</code> / <code>${(L)var}</code></h3>
<p>Lowercase. Same dual-form pattern as upper.</p>
<pre><code>x=HELLO
echo ${x:l} # hello
echo ${(L)x} # hello</code></pre>
</article>
<article class="doc-entry" id="doc-pe-special-q">
<h3><a class="doc-anchor" href="#doc-pe-special-q">#</a> <code>$?</code></h3>
<p>Exit status of the last command. Routed through <code>BUILTIN_GET_VAR("?")</code> which reads <code>vm.last_status</code> synced into the executor.</p>
<pre><code>cargo test
if (( $? != 0 )); then echo "tests failed"; fi</code></pre>
</article>
<article class="doc-entry" id="doc-pe-special-pid">
<h3><a class="doc-anchor" href="#doc-pe-special-pid">#</a> <code>$$</code></h3>
<p>Current shell PID (<code>std::process::id()</code>).</p>
<pre><code>tmpdir=/tmp/build_$$
mkdir -p "$tmpdir"</code></pre>
</article>
<article class="doc-entry" id="doc-pe-special-positional">
<h3><a class="doc-anchor" href="#doc-pe-special-positional">#</a> <code>$1 $2 … $@ $* $#</code></h3>
<p>Positional parameters. <code>$@</code> / <code>$*</code> expand to the full list (joined by IFS), <code>$#</code> is the count, numeric <code>$N</code> is the Nth positional. Native fast-path: <code>"$@"</code> and <code>${arr[@]}</code> reach <code>BUILTIN_GET_VAR</code> / <code>BUILTIN_ARRAY_ALL</code> as <code>Value::Array</code> and spread through <code>BUILTIN_ARRAY_FLATTEN</code>.</p>
<pre><code>greet() {
echo "got $# args"
for arg in "$@"; do echo "- $arg"; done
}
greet alpha "two words" gamma</code></pre>
</article>
<article class="doc-entry" id="doc-pe-special-misc">
<h3><a class="doc-anchor" href="#doc-pe-special-misc">#</a> <code>$! $_ $-</code></h3>
<p>Auxiliary special params. <code>$!</code> = PID of the most recent backgrounded job. <code>$_</code> = last argument of the previous command (or empty at script start). <code>$-</code> = current shell flag set as a single string. All routed through <code>BUILTIN_GET_VAR</code>.</p>
</article>
<article class="doc-entry" id="doc-pe-random">
<h3><a class="doc-anchor" href="#doc-pe-random">#</a> <code>$RANDOM $SECONDS $EPOCHSECONDS $LINENO</code></h3>
<p>Dynamic special parameters resolved on each read in <code>get_variable</code>:</p>
<ul>
<li><code>$RANDOM</code> — 15-bit pseudorandom int. Mixes nanos+pid via Knuth's hash, masked to 15 bits. No seedable state today (independent draws).</li>
<li><code>$SECONDS</code> — seconds since shell start, derived from <code>__zshrs_start_secs</code> baseline.</li>
<li><code>$EPOCHSECONDS</code> — Unix time, <code>SystemTime::now()</code>.</li>
<li><code>$LINENO</code> — current line in the script (falls back to 1 today; full line tracking is Phase G work).</li>
</ul>
<pre><code>tmp="/tmp/build_$$_$RANDOM"
echo "elapsed: $SECONDS s"
echo "now: $EPOCHSECONDS"</code></pre>
</article>
<article class="doc-entry" id="doc-pe-pipestatus">
<h3><a class="doc-anchor" href="#doc-pe-pipestatus">#</a> <code>$PIPESTATUS</code> / <code>$pipestatus</code></h3>
<p>Per-stage exit codes of the most recent pipeline. <code>BUILTIN_RUN_PIPELINE</code> collects each stage's status and writes both <code>pipestatus</code> (zsh convention) and <code>PIPESTATUS</code> (bash convention). Useful for <code>set -e</code>-style scripts that need to differentiate which stage failed.</p>
<pre><code>seq 100 | grep '^5' | wc -l
echo "${pipestatus[@]}" # 0 0 0
false | true
echo "${pipestatus[1]}" # 1 (zsh: 1-indexed)</code></pre>
</article>
<article class="doc-entry" id="doc-pe-var-exists">
<h3><a class="doc-anchor" href="#doc-pe-var-exists">#</a> <code>[[ -v var ]]</code> — existence test</h3>
<p><code>BUILTIN_VAR_EXISTS</code> (id 306) checks every binding table — scalar <code>variables</code>, <code>arrays</code>, <code>assoc_arrays</code>, plus the process env — and returns true if <em>any</em> matches. Useful for "set but empty" vs "unset" disambiguation that <code>-z</code> conflates.</p>
<pre><code>x=""
[[ -v x ]] && echo "set" # set (even though empty)
[[ -z $x ]] && echo "empty" # empty
unset x
[[ -v x ]] || echo "unset" # unset</code></pre>
</article>
<article class="doc-entry" id="doc-pe-arith-sub">
<h3><a class="doc-anchor" href="#doc-pe-arith-sub">#</a> <code>$((expr))</code></h3>
<p>Arithmetic substitution. The expression compiles through the inline arithmetic compiler — no runtime parser invocation. Integers are <code>i64</code>; supports <code>+ - * / % ** & | ^ << >> && ||</code> and pre/post inc/dec.</p>
<pre><code>(( total = 1 + 2 * 3 ))
echo $((2 ** 16)) # 65536
echo $((0xff & 0x0f))</code></pre>
</article>
<article class="doc-entry" id="doc-pe-cmd-sub">
<h3><a class="doc-anchor" href="#doc-pe-cmd-sub">#</a> <code>$(cmd)</code> / <code>` cmd `</code></h3>
<p>Command substitution. zshrs runs the inner command on a nested VM and captures stdout via <code>os_pipe::pipe()</code> + <code>dup2</code> — <em>no fork</em>. Trailing newlines are stripped per POSIX.</p>
<pre><code>now=$(date +%s)
files=$(ls *.rs | wc -l)
echo "$files files at $now"</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-zsh-flags">
<h2>Zsh Parameter Flags</h2>
<p class="chapter-meta">12 topics · <code>${(flags)name}</code> form. Flags apply left-to-right; <code>(jL)</code> joins-then-lowercases, <code>(s:,:U)</code> splits-then-uppercases. Routes through <code>BUILTIN_PARAM_FLAG</code> (id 297).</p>
<article class="doc-entry" id="doc-flag-L">
<h3><a class="doc-anchor" href="#doc-flag-L">#</a> <code>(L)</code> — lowercase</h3>
<p>Lowercases the value. For arrays, lowercases each element.</p>
<pre><code>x=Hello
echo ${(L)x} # hello
arr=(One Two Three)
echo ${(L)arr} # one two three</code></pre>
</article>
<article class="doc-entry" id="doc-flag-U">
<h3><a class="doc-anchor" href="#doc-flag-U">#</a> <code>(U)</code> — uppercase</h3>
<p>Uppercases.</p>
<pre><code>echo ${(U)x} # HELLO</code></pre>
</article>
<article class="doc-entry" id="doc-flag-j">
<h3><a class="doc-anchor" href="#doc-flag-j">#</a> <code>(j:sep:)</code> — join</h3>
<p>Join array elements with <code>sep</code>. The delimiter char following <code>j</code> sets the bracket — <code>:</code>, <code>.</code>, <code>,</code>, <code>|</code> are common. <code>j</code> with no delimiter joins with a single space (IFS-default).</p>
<pre><code>arr=(one two three)
echo ${(j:-:)arr} # one-two-three
echo ${(j:|:)arr} # one|two|three
echo ${(j)arr} # one two three</code></pre>
</article>
<article class="doc-entry" id="doc-flag-s">
<h3><a class="doc-anchor" href="#doc-flag-s">#</a> <code>(s:sep:)</code> — split</h3>
<p>Split a scalar on <code>sep</code> into an array. The result splices into argv via the standard array-flatten path.</p>
<pre><code>csv="alpha,beta,gamma"
for x in "${(s:,:)csv}"; do
echo "[$x]"
done</code></pre>
</article>
<article class="doc-entry" id="doc-flag-f">
<h3><a class="doc-anchor" href="#doc-flag-f">#</a> <code>(f)</code> — split on newlines</h3>
<p>Shorthand for <code>(s:\n:)</code> — split on newlines. Common with command substitution.</p>
<pre><code>for line in "${(f)$(ls)}"; do
echo "FILE: $line"
done</code></pre>
</article>
<article class="doc-entry" id="doc-flag-o">
<h3><a class="doc-anchor" href="#doc-flag-o">#</a> <code>(o)</code> — sort ascending</h3>
<p>Lexicographic sort.</p>
<pre><code>arr=(charlie alpha bravo)
echo ${(o)arr} # alpha bravo charlie</code></pre>
</article>
<article class="doc-entry" id="doc-flag-O">
<h3><a class="doc-anchor" href="#doc-flag-O">#</a> <code>(O)</code> — sort descending</h3>
<p>Reverse lexicographic sort.</p>
<pre><code>echo ${(O)arr} # charlie bravo alpha</code></pre>
</article>
<article class="doc-entry" id="doc-flag-P">
<h3><a class="doc-anchor" href="#doc-flag-P">#</a> <code>(P)</code> — indirect</h3>
<p>Use the value of the variable as <em>another</em> variable name and look that up. Useful for dynamic dispatch tables.</p>
<pre><code>real=42
ref=real
echo ${(P)ref} # 42</code></pre>
</article>
<article class="doc-entry" id="doc-flag-at">
<h3><a class="doc-anchor" href="#doc-flag-at">#</a> <code>(@)</code> — force array</h3>
<p>Coerce a scalar to a single-element array (so subsequent flags treat it as array-shape).</p>
<pre><code>x=hello
echo ${#x} # 5 (string length)
echo ${(#)${(@)x}} # 1 (array length after @-coerce)</code></pre>
</article>
<article class="doc-entry" id="doc-flag-k">
<h3><a class="doc-anchor" href="#doc-flag-k">#</a> <code>(k)</code> — keys of assoc</h3>
<p>Returns the keys of an associative array as an array. Order is HashMap iteration order.</p>
<pre><code>typeset -A m
m[apple]=1; m[banana]=2
for k in "${(k)m}"; do echo "$k"; done</code></pre>
</article>
<article class="doc-entry" id="doc-flag-v">
<h3><a class="doc-anchor" href="#doc-flag-v">#</a> <code>(v)</code> — values of assoc</h3>
<p>Returns the values of an associative array as an array.</p>
<pre><code>echo ${(v)m} # 1 2</code></pre>
</article>
<article class="doc-entry" id="doc-flag-pound">
<h3><a class="doc-anchor" href="#doc-flag-pound">#</a> <code>(#)</code> — count</h3>
<p>Element count for arrays, character count for scalars. Different from <code>${#var}</code> in that it can be combined with other flags via stacking.</p>
<pre><code>arr=(a b c d)
echo ${(#)arr} # 4
echo ${(#L)x} # length after lowercasing</code></pre>
</article>
<article class="doc-entry" id="doc-flag-q">
<h3><a class="doc-anchor" href="#doc-flag-q">#</a> <code>(q)</code> / <code>(qq)</code> / <code>(qqq)</code> — quote</h3>
<p>Wrap each value with shell-safe quoting. Consecutive <code>q</code>s raise the quoting level: <code>q</code> = POSIX single-quote (escaping inner <code>'</code>), <code>qq</code> = double-quote (escaping <code>$</code>/<code>`</code>/<code>"</code>/<code>\</code>), <code>qqq</code> = ANSI-C <code>$'…'</code> form (escaping control chars + backslashes).</p>
<pre><code>x="hi 'world'"
echo ${(q)x} # 'hi '\''world'\'''
echo ${(qq)x} # "hi 'world'"
s=$(printf 'a\tb')
echo ${(qqq)s} # $'a\tb'</code></pre>
</article>
<article class="doc-entry" id="doc-flag-g">
<h3><a class="doc-anchor" href="#doc-flag-g">#</a> <code>(g)</code> — backslash-escape unwrap</h3>
<p>Process backslash escapes (<code>\n</code>, <code>\t</code>, <code>\r</code>, <code>\\</code>, <code>\xNN</code>). Useful when reading lines from a config that stored real newlines as literal <code>\n</code>.</p>
<pre><code>s='hello\nworld'
echo "${(g)s}"
# hello
# world</code></pre>
</article>
<article class="doc-entry" id="doc-flag-n">
<h3><a class="doc-anchor" href="#doc-flag-n">#</a> <code>(n)</code> — natural-numeric sort</h3>
<p>Compare digit runs as integers and other runs lexicographically. <code>file2</code> sorts before <code>file10</code> (vs lex order which would put <code>file10</code> first). Combine with <code>(o)</code>/<code>(O)</code> for ascending/descending.</p>
<pre><code>arr=(file10 file2 file1 file20)
echo ${(on)arr} # file1 file2 file10 file20</code></pre>
</article>
<article class="doc-entry" id="doc-flag-i">
<h3><a class="doc-anchor" href="#doc-flag-i">#</a> <code>(i)</code> — case-insensitive sort</h3>
<p>Sort comparing lowercase forms while preserving original case in the output.</p>
<pre><code>arr=(Banana apple Cherry)
echo ${(i)arr} # apple Banana Cherry</code></pre>
</article>
<article class="doc-entry" id="doc-flag-t">
<h3><a class="doc-anchor" href="#doc-flag-t">#</a> <code>(t)</code> — type query</h3>
<p>Returns the variable's typeset shape: <code>scalar</code>, <code>array</code>, <code>association</code>, or empty (unset). Useful in scripts that branch on whether a name is an indexed vs assoc array.</p>
<pre><code>arr=(a b)
typeset -A m; m[k]=v
sc=str
echo "${(t)arr}|${(t)m}|${(t)sc}"
# array|association|scalar</code></pre>
</article>
<article class="doc-entry" id="doc-flag-percent">
<h3><a class="doc-anchor" href="#doc-flag-percent">#</a> <code>(%)</code> — prompt expansion</h3>
<p>Process <code>%F</code>/<code>%B</code>/<code>%f</code>/<code>%{</code>/<code>%}</code> etc. via <code>expand_prompt_string</code>. Useful for storing prompt fragments in variables and rendering them at use time.</p>
<pre><code>fragment='%F{cyan}>>%f '
echo "${(%)fragment}"</code></pre>
</article>
<article class="doc-entry" id="doc-flag-e">
<h3><a class="doc-anchor" href="#doc-flag-e">#</a> <code>(e)</code> — re-evaluate</h3>
<p>Run the value as a shell command and return its captured stdout. Equivalent to <code>$(eval "$value")</code> but uses the in-process pipe-capture path (no fork). Late-bound config strings.</p>
<pre><code>cmd='echo "user is $(whoami)"'
echo "${(e)cmd}" # user is jacob</code></pre>
</article>
<article class="doc-entry" id="doc-flag-p">
<h3><a class="doc-anchor" href="#doc-flag-p">#</a> <code>(p)</code> — print-style escapes</h3>
<p>Process backslash escapes the same way <code>print -e</code> does. Same set as <code>(g)</code> with <code>\e</code> / <code>\E</code> mapped to <code>ESC</code>.</p>
<pre><code>fmt='\e[36mcolor\e[0m'
echo "${(p)fmt}" # cyan "color"</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-redirects-pipes">
<h2>Redirection & Pipelines</h2>
<p class="chapter-meta">14 topics · pipelines fork-per-stage via <code>BUILTIN_RUN_PIPELINE</code> (id 285); redirects scoped via <code>WithRedirectsBegin/End</code>.</p>
<article class="doc-entry" id="doc-redir-write">
<h3><a class="doc-anchor" href="#doc-redir-write">#</a> <code>cmd > file</code> — write</h3>
<p>Truncates and writes stdout to file. Bracketed in a redirect scope so subsequent commands see the original fd 1.</p>
<pre><code>echo hello > out.txt</code></pre>
</article>
<article class="doc-entry" id="doc-redir-append">
<h3><a class="doc-anchor" href="#doc-redir-append">#</a> <code>cmd >> file</code> — append</h3>
<p>Appends stdout. Creates the file if missing.</p>
<pre><code>date >> access.log</code></pre>
</article>
<article class="doc-entry" id="doc-redir-read">
<h3><a class="doc-anchor" href="#doc-redir-read">#</a> <code>cmd < file</code> — read</h3>
<p>Connects file to stdin.</p>
<pre><code>sort < data.txt</code></pre>
</article>
<article class="doc-entry" id="doc-redir-stderr">
<h3><a class="doc-anchor" href="#doc-redir-stderr">#</a> <code>cmd 2> file</code> — stderr</h3>
<p>Redirect stderr (fd 2). Use <code>2>&1</code> to merge into stdout.</p>
<pre><code>cargo build 2> build.err
make 2>&1 | tee build.log</code></pre>
</article>
<article class="doc-entry" id="doc-redir-write-both">
<h3><a class="doc-anchor" href="#doc-redir-write-both">#</a> <code>cmd &> file</code> — write both</h3>
<p>Redirect both stdout and stderr (zsh/bash extension). <code>&>></code> appends.</p>
<pre><code>./run.sh &> combined.log</code></pre>
</article>
<article class="doc-entry" id="doc-redir-heredoc">
<h3><a class="doc-anchor" href="#doc-redir-heredoc">#</a> <code>cmd <<EOF</code> — heredoc (with variants)</h3>
<p>Multi-line stdin literal. Three flavors:</p>
<ul style="margin-top: 0.4rem;">
<li><code><<EOF</code> — variables, command sub, arithmetic all expand inside the body. Body trailing newline trimmed before <code>HereString</code> emit so stdin is byte-identical to source.</li>
<li><code><<-EOF</code> — leading tabs stripped from each body line <em>and</em> from the closing-terminator-line check. Lets you indent the heredoc body inside a function body.</li>
<li><code><<'EOF'</code> / <code><<"EOF"</code> — quoted terminator → body is verbatim, <strong>no</strong> expansion. Detected by SNULL/DNULL markers in the lexer's terminator string; <code>HereDocInfo.quoted</code> drives compile-side behavior.</li>
</ul>
<p style="margin-top: 0.4rem;">Body capture happens in a parser post-pass (<code>fill_heredoc_bodies</code>) — the lexer collects bodies into <code>self.heredocs[]</code> at <code>process_heredocs</code> (called on Newlin/Endinput); the parser records a <code>heredoc_idx</code> per redir during <code>parse_redirection</code>; the post-pass walks the AST resolving indices into <code>ZshRedir.heredoc</code>.</p>
<pre><code>cat <<EOF
hello $USER
host: $(hostname)
EOF
cat <<-EOF
indented
body
EOF
cat <<'EOF'
$USER stays literal
EOF</code></pre>
</article>
<article class="doc-entry" id="doc-redir-herestring">
<h3><a class="doc-anchor" href="#doc-redir-herestring">#</a> <code>cmd <<< "string"</code> — herestring</h3>
<p>Pass a single string (with trailing newline) as stdin.</p>
<pre><code>tr a-z A-Z <<< "hello" # HELLO</code></pre>
</article>
<article class="doc-entry" id="doc-redir-pipe">
<h3><a class="doc-anchor" href="#doc-redir-pipe">#</a> <code>cmd1 | cmd2</code> — pipeline</h3>
<p>Connect cmd1's stdout to cmd2's stdin via a pipe. zshrs's pipeline is bytecode-native: each stage compiles to a sub-chunk; <code>BUILTIN_RUN_PIPELINE</code> creates N-1 pipes, forks N children, wires fds, runs each stage's bytecode on a fresh VM. SIGPIPE works correctly.</p>
<pre><code>seq 100 | sort | uniq | wc -l</code></pre>
</article>
<article class="doc-entry" id="doc-redir-pipe-stderr">
<h3><a class="doc-anchor" href="#doc-redir-pipe-stderr">#</a> <code>cmd1 |& cmd2</code> — pipe stdout+stderr</h3>
<p>zsh extension: pipes both fd 1 and fd 2 of the LHS to RHS.</p>
<pre><code>cargo test |& head -40</code></pre>
</article>
<article class="doc-entry" id="doc-redir-not">
<h3><a class="doc-anchor" href="#doc-redir-not">#</a> <code>! cmd</code> — invert status</h3>
<p>Negate the pipeline's exit status. <code>! cmd</code> exits 0 if cmd failed, 1 if cmd succeeded.</p>
<pre><code>! grep -q ERROR log # status 0 means no ERROR found</code></pre>
</article>
<article class="doc-entry" id="doc-redir-procsub-in">
<h3><a class="doc-anchor" href="#doc-redir-procsub-in">#</a> <code><(cmd)</code> — process sub (input)</h3>
<p>Spawn cmd, present its stdout as a path readable by the consumer. zshrs uses worker pool threads instead of fork — the FIFO/temp file is wired to a thread that runs the bytecode for cmd.</p>
<pre><code>diff <(sort a.txt) <(sort b.txt)</code></pre>
</article>
<article class="doc-entry" id="doc-redir-procsub-out">
<h3><a class="doc-anchor" href="#doc-redir-procsub-out">#</a> <code>>(cmd)</code> — process sub (output)</h3>
<p>Spawn cmd, present its stdin as a path writable by the producer.</p>
<pre><code>tar c . | tee >(gzip > backup.tgz) > backup.tar</code></pre>
</article>
<article class="doc-entry" id="doc-redir-fd-dup">
<h3><a class="doc-anchor" href="#doc-redir-fd-dup">#</a> <code>cmd N>&M</code> — duplicate fd</h3>
<p>Duplicate fd M to fd N. Common idiom: <code>2>&1</code> merges stderr into stdout. <code><&fd</code> defaults the destination to fd 0; <code>>&fd</code> defaults to fd 1. Variable-expanded fd targets work: <code>read line <&${COPROC[1]}</code>.</p>
<pre><code>find / 2>&1 | grep '\.zsh$' | head
coproc { echo from-child; }
read reply <&${COPROC[1]}
echo "$reply" # from-child</code></pre>
</article>
<article class="doc-entry" id="doc-redir-fd-close">
<h3><a class="doc-anchor" href="#doc-redir-fd-close">#</a> <code>cmd N<&-</code> / <code>cmd N>&-</code> — close fd</h3>
<p>Close fd N. POSIX form for explicitly tearing down a previously-opened fd (typically after <code>exec FD< file</code>).</p>
<pre><code>exec 5< data.txt
read line <&5
exec 5<&- # close fd 5</code></pre>
</article>
<article class="doc-entry" id="doc-redir-with-redirects">
<h3><a class="doc-anchor" href="#doc-redir-with-redirects">#</a> <code>{ cmds; } > file</code> — block redirect</h3>
<p>Apply a redirect to a compound command. <code>WithRedirectsBegin/End</code> bracket the inner ops so the parent shell's fds are restored after.</p>
<pre><code>{
echo header
date
uname -a
} > report.txt</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-shell-builtins">
<h2>POSIX / Zsh Builtins</h2>
<p class="chapter-meta">52 topics · the full POSIX-required set plus zsh's interactive-shell builtins. Every entry routes through fusevm <code>CallBuiltin(id, argc)</code>; arrays splice via <code>pop_args</code>.</p>
<article class="doc-entry" id="doc-cd">
<h3><a class="doc-anchor" href="#doc-cd">#</a> <code>cd [dir]</code></h3>
<p>Change directory. With no argument, goes to <code>$HOME</code>. <code>cd -</code> jumps to <code>$OLDPWD</code>. Updates <code>$PWD</code>.</p>
<pre><code>cd ~/repos/zshrs
cd - # back to previous</code></pre>
</article>
<article class="doc-entry" id="doc-pwd">
<h3><a class="doc-anchor" href="#doc-pwd">#</a> <code>pwd</code></h3>
<p>Print working directory.</p>
<pre><code>echo "now in $(pwd)"</code></pre>
</article>
<article class="doc-entry" id="doc-echo">
<h3><a class="doc-anchor" href="#doc-echo">#</a> <code>echo [args]</code></h3>
<p>Print arguments space-separated with trailing newline. <code>-n</code> suppresses newline; <code>-e</code> enables backslash escapes.</p>
<pre><code>echo hello world
echo -n "no newline"
echo -e "tab:\there"</code></pre>
</article>
<article class="doc-entry" id="doc-print">
<h3><a class="doc-anchor" href="#doc-print">#</a> <code>print</code></h3>
<p>zsh's enhanced echo. <code>-r</code> raw, <code>-n</code> no newline, <code>-l</code> one arg per line, <code>-z</code> push to history.</p>
<pre><code>print -l alpha beta gamma # one per line
print -P "%F{cyan}cyan%f" # prompt expansion</code></pre>
</article>
<article class="doc-entry" id="doc-printf">
<h3><a class="doc-anchor" href="#doc-printf">#</a> <code>printf format args…</code></h3>
<p>C-style formatted output. Format directives: <code>%s %d %f %x %o %b</code>; backslash escapes <code>\n \t</code> etc.</p>
<pre><code>printf "%-10s %5d\n" "items" 42</code></pre>
</article>
<article class="doc-entry" id="doc-export">
<h3><a class="doc-anchor" href="#doc-export">#</a> <code>export VAR=value</code></h3>
<p>Mark a variable for export to subprocesses (writes to <code>std::env</code>).</p>
<pre><code>export PATH="/opt/bin:$PATH"
export RUST_LOG=debug</code></pre>
</article>
<article class="doc-entry" id="doc-unset">
<h3><a class="doc-anchor" href="#doc-unset">#</a> <code>unset VAR…</code></h3>
<p>Remove a variable. Removes from <code>variables</code>, <code>arrays</code>, <code>assoc_arrays</code>, and exported env.</p>
<pre><code>unset DEBUG_FLAG
unset -f myfunc # remove function</code></pre>
</article>
<article class="doc-entry" id="doc-source">
<h3><a class="doc-anchor" href="#doc-source">#</a> <code>source file</code> / <code>. file</code></h3>
<p>Execute a script in the current shell. zshrs's source path uses bytecode caching: first run compiles + stores to SQLite; subsequent runs deserialize cached chunks. Plugin delta cache replays state changes (functions, aliases, vars, hooks, zstyles, options) in microseconds.</p>
<pre><code>source ~/.zshrc
. /etc/profile.d/lang.sh</code></pre>
</article>
<article class="doc-entry" id="doc-exit">
<h3><a class="doc-anchor" href="#doc-exit">#</a> <code>exit [n]</code></h3>
<p>Exit the shell with status <em>n</em> (default last status). Aliases: <code>bye</code>, <code>logout</code>. Compile emits a forward jump patched past chunk-end (halts the VM). EXIT trap fires before halt.</p>
<pre><code>[[ -z $1 ]] && { echo "missing arg"; exit 2; }</code></pre>
</article>
<article class="doc-entry" id="doc-true-false">
<h3><a class="doc-anchor" href="#doc-true-false">#</a> <code>true</code> / <code>false</code> / <code>:</code></h3>
<p>Status helpers. <code>true</code> and <code>:</code> exit 0; <code>false</code> exits 1. <code>:</code> is also the no-op for argument expansion side effects.</p>
<pre><code>: ${VAR:=default} # set if unset; expansion side effect, no command
while true; do …; done</code></pre>
</article>
<article class="doc-entry" id="doc-test">
<h3><a class="doc-anchor" href="#doc-test">#</a> <code>test EXPR</code> / <code>[ EXPR ]</code></h3>
<p>POSIX conditional test. Use <code>[[ … ]]</code> for the zsh extended form (in-shell, no fork). Operators: <code>-f -d -e -r -w -x -s -z -n = != < > -eq -ne -lt -le -gt -ge</code>.</p>
<pre><code>[ -f /etc/passwd ] && echo "exists"
[[ $count -gt 10 && -n $name ]]</code></pre>
</article>
<article class="doc-entry" id="doc-local">
<h3><a class="doc-anchor" href="#doc-local">#</a> <code>local VAR[=value]</code></h3>
<p>Function-local scope. zshrs's local-scope semantics: declared vars are pushed onto a scope stack at function entry, popped at return. Assignment-without-declare-at-top-of-function is allowed (zsh idiom) but has caveats — see CLAUDE.md "Never use `local` inside loops".</p>
<pre><code>my_func() {
local count=0
local result
…
}</code></pre>
</article>
<article class="doc-entry" id="doc-typeset">
<h3><a class="doc-anchor" href="#doc-typeset">#</a> <code>typeset</code> / <code>declare</code></h3>
<p>Declare a variable with attributes. <code>-a</code> indexed array, <code>-A</code> assoc array, <code>-i</code> integer, <code>-r</code> readonly, <code>-x</code> exported, <code>-l</code> lowercase, <code>-u</code> uppercase, <code>-g</code> global (in func scope), <code>-f</code> function-scoped.</p>
<pre><code>typeset -i count=0
typeset -A config
typeset -r APP_VERSION=1.0
declare -a names=(alice bob carol)</code></pre>
</article>
<article class="doc-entry" id="doc-readonly">
<h3><a class="doc-anchor" href="#doc-readonly">#</a> <code>readonly VAR=val</code></h3>
<p>Mark a variable read-only. Subsequent assignments error.</p>
<pre><code>readonly RELEASE_BRANCH=main</code></pre>
</article>
<article class="doc-entry" id="doc-integer">
<h3><a class="doc-anchor" href="#doc-integer">#</a> <code>integer VAR=expr</code></h3>
<p>Declare an integer-typed variable. Subsequent assignments evaluate as arithmetic.</p>
<pre><code>integer i=10
i=i+5
echo $i # 15</code></pre>
</article>
<article class="doc-entry" id="doc-float">
<h3><a class="doc-anchor" href="#doc-float">#</a> <code>float VAR=expr</code></h3>
<p>Declare a float-typed variable.</p>
<pre><code>float pi=3.14159
echo $((pi * 2))</code></pre>
</article>
<article class="doc-entry" id="doc-read">
<h3><a class="doc-anchor" href="#doc-read">#</a> <code>read VAR…</code></h3>
<p>Read a line from stdin into one or more variables. Flags: <code>-r</code> raw (no backslash escapes), <code>-n N</code> N chars, <code>-t SEC</code> timeout, <code>-s</code> silent, <code>-p prompt</code>, <code>-A arr</code> read into array.</p>
<pre><code>echo -n "name? "
read name
read -r line < /etc/hostname
read -A words <<< "alpha beta gamma"</code></pre>
</article>
<article class="doc-entry" id="doc-mapfile">
<h3><a class="doc-anchor" href="#doc-mapfile">#</a> <code>mapfile</code> / <code>readarray</code></h3>
<p>Read each line of a file (or stdin) into successive elements of an array. Bash compat alias.</p>
<pre><code>mapfile -t lines < /etc/hosts
echo "${#lines[@]} hosts"</code></pre>
</article>
<article class="doc-entry" id="doc-shift">
<h3><a class="doc-anchor" href="#doc-shift">#</a> <code>shift [n]</code></h3>
<p>Remove the first <em>n</em> positional parameters (default 1).</p>
<pre><code>process() {
cmd=$1; shift
do_thing "$cmd" "$@"
}</code></pre>
</article>
<article class="doc-entry" id="doc-eval">
<h3><a class="doc-anchor" href="#doc-eval">#</a> <code>eval string</code></h3>
<p>Re-parse and execute the joined-args string as shell code. Single-quoted args defer expansion correctly (the lexer's <code>\0</code>-sentinel for single-quoted specials is honored by the compile path's trigger detection).</p>
<pre><code>x=10
eval 'echo $x' # 10 (expansion deferred to eval-time)
eval "$(starship init zsh)"</code></pre>
</article>
<article class="doc-entry" id="doc-exec">
<h3><a class="doc-anchor" href="#doc-exec">#</a> <code>exec [cmd args]</code></h3>
<p>Replace the current shell with <em>cmd</em>. With no command, applies redirections to the current shell (e.g. <code>exec > log</code> redirects subsequent output).</p>
<pre><code>exec > build.log 2>&1
exec /usr/local/bin/newshell</code></pre>
</article>
<article class="doc-entry" id="doc-command">
<h3><a class="doc-anchor" href="#doc-command">#</a> <code>command [-pvV] cmd</code></h3>
<p>Bypass functions/aliases and run the underlying command. <code>-v</code> prints how the name would resolve; <code>-V</code> verbose form.</p>
<pre><code>command rm /tmp/junk # bypass user's `rm` function
command -v cargo # /Users/wizard/.cargo/bin/cargo</code></pre>
</article>
<article class="doc-entry" id="doc-builtin">
<h3><a class="doc-anchor" href="#doc-builtin">#</a> <code>builtin cmd</code></h3>
<p>Force builtin dispatch (skip functions/aliases).</p>
<pre><code>builtin cd /tmp # zshrs's cd, never a user override</code></pre>
</article>
<article class="doc-entry" id="doc-let">
<h3><a class="doc-anchor" href="#doc-let">#</a> <code>let "expr"</code></h3>
<p>Evaluate arithmetic expression(s); exit 0 if last result is non-zero.</p>
<pre><code>let "i = 1 + 2"
let "i++"</code></pre>
</article>
<article class="doc-entry" id="doc-set">
<h3><a class="doc-anchor" href="#doc-set">#</a> <code>set [-eux] [args…]</code></h3>
<p>Set shell options or positional parameters. <code>-e</code> exit on error, <code>-u</code> nounset, <code>-x</code> trace, <code>-o pipefail</code> propagate pipeline status.</p>
<pre><code>set -euo pipefail
set -- alpha beta gamma # set $1 $2 $3</code></pre>
</article>
<article class="doc-entry" id="doc-setopt-unsetopt">
<h3><a class="doc-anchor" href="#doc-setopt-unsetopt">#</a> <code>setopt</code> / <code>unsetopt</code></h3>
<p>Toggle zsh-style options. Options live in <code>executor.options</code>; <code>setopt foo</code> turns on, <code>unsetopt foo</code> turns off.</p>
<pre><code>setopt extended_glob
setopt null_glob
unsetopt nomatch</code></pre>
</article>
<article class="doc-entry" id="doc-shopt">
<h3><a class="doc-anchor" href="#doc-shopt">#</a> <code>shopt</code></h3>
<p>Bash-compat option toggling. Maps onto the same options table as <code>setopt</code>.</p>
<pre><code>shopt -s globstar</code></pre>
</article>
<article class="doc-entry" id="doc-emulate">
<h3><a class="doc-anchor" href="#doc-emulate">#</a> <code>emulate [-LR] [shell]</code></h3>
<p>Switch emulation mode (<code>zsh</code>, <code>sh</code>, <code>ksh</code>, <code>csh</code>). <code>-L</code> local-to-function, <code>-R</code> reset all options to that shell's defaults.</p>
<pre><code>emulate -L zsh # function-local zsh defaults</code></pre>
</article>
<article class="doc-entry" id="doc-getopts">
<h3><a class="doc-anchor" href="#doc-getopts">#</a> <code>getopts spec var</code></h3>
<p>POSIX option parsing. Iterates flags from <code>$@</code>, sets <code>$var</code> to each option, <code>$OPTARG</code> to its value.</p>
<pre><code>while getopts "vh:" opt; do
case $opt in
v) verbose=1 ;;
h) host=$OPTARG ;;
esac
done</code></pre>
</article>
<article class="doc-entry" id="doc-zparseopts">
<h3><a class="doc-anchor" href="#doc-zparseopts">#</a> <code>zparseopts</code></h3>
<p>zsh's richer option parser. <code>-A arr</code> stores into an assoc array; supports long options.</p>
<pre><code>zparseopts -A opts -- v h: c::
echo "${opts[-h]}"</code></pre>
</article>
<article class="doc-entry" id="doc-autoload">
<h3><a class="doc-anchor" href="#doc-autoload">#</a> <code>autoload [name…]</code></h3>
<p>Mark a function for lazy loading from <code>$fpath</code>. zshrs's autoload path uses SQLite-cached bytecodes: first invocation deserializes a chunk; subsequent invocations skip lex/parse/compile entirely.</p>
<pre><code>autoload -Uz compinit
compinit</code></pre>
</article>
<article class="doc-entry" id="doc-functions">
<h3><a class="doc-anchor" href="#doc-functions">#</a> <code>functions [name]</code></h3>
<p>List defined functions, or print a function's body. <code>+f</code> prints names only.</p>
<pre><code>functions # all functions
functions my_func # body of my_func</code></pre>
</article>
<article class="doc-entry" id="doc-unfunction">
<h3><a class="doc-anchor" href="#doc-unfunction">#</a> <code>unfunction name</code></h3>
<p>Remove a function definition. Same as <code>unset -f</code>.</p>
<pre><code>unfunction old_helper</code></pre>
</article>
<article class="doc-entry" id="doc-trap">
<h3><a class="doc-anchor" href="#doc-trap">#</a> <code>trap 'cmd' SIG</code></h3>
<p>Install a signal handler. Special pseudo-signals: <code>EXIT</code> (run at shell exit), <code>DEBUG</code> (before each command), <code>ERR</code> (on non-zero status), <code>ZERR</code> (zsh-specific).</p>
<pre><code>trap 'rm -rf $tmpdir' EXIT
trap 'echo got SIGINT' INT</code></pre>
</article>
<article class="doc-entry" id="doc-pushd-popd">
<h3><a class="doc-anchor" href="#doc-pushd-popd">#</a> <code>pushd</code> / <code>popd</code> / <code>dirs</code></h3>
<p>Directory stack manipulation. <code>pushd dir</code> changes to <em>dir</em> and pushes onto the stack; <code>popd</code> pops and changes back. <code>dirs -v</code> lists.</p>
<pre><code>pushd ~/work/proj
…
popd</code></pre>
</article>
<article class="doc-entry" id="doc-alias">
<h3><a class="doc-anchor" href="#doc-alias">#</a> <code>alias name='cmd'</code></h3>
<p>Define a command alias. With no args lists all aliases. <code>alias -g</code> creates a global alias (substitutes anywhere on the command line).</p>
<pre><code>alias ll='ls -lAh'
alias -g G='| grep'</code></pre>
</article>
<article class="doc-entry" id="doc-unalias">
<h3><a class="doc-anchor" href="#doc-unalias">#</a> <code>unalias [-m pattern] name</code></h3>
<p>Remove an alias. <code>-m</code> matches by glob pattern.</p>
<pre><code>unalias ll
unalias -m 'g*' # remove all aliases starting with g</code></pre>
</article>
<article class="doc-entry" id="doc-type-whence-which">
<h3><a class="doc-anchor" href="#doc-type-whence-which">#</a> <code>type</code> / <code>whence</code> / <code>where</code> / <code>which</code></h3>
<p>Resolve a name to its source: alias, function, builtin, or external. <code>whence -p</code> shows path only; <code>whence -a</code> shows all matches.</p>
<pre><code>type ls
whence -a cargo
which gcc</code></pre>
</article>
<article class="doc-entry" id="doc-hash">
<h3><a class="doc-anchor" href="#doc-hash">#</a> <code>hash</code> / <code>rehash</code> / <code>unhash</code></h3>
<p>Command-name → path cache. <code>rehash</code> rebuilds the cache from <code>$PATH</code> (parallel scan across worker pool). <code>unhash -dm 'pat'</code> removes named-directory entries by glob.</p>
<pre><code>rehash # after installing new binaries
hash # show cache
unhash -dm 'tmp*' # remove tmp* hashed dir refs</code></pre>
</article>
<article class="doc-entry" id="doc-ulimit">
<h3><a class="doc-anchor" href="#doc-ulimit">#</a> <code>ulimit</code> / <code>limit</code> / <code>unlimit</code></h3>
<p>Resource limit query/set. <code>-n</code> open files, <code>-s</code> stack size, <code>-u</code> max processes, <code>-c</code> core dump.</p>
<pre><code>ulimit -n 65536
ulimit -a # show all</code></pre>
</article>
<article class="doc-entry" id="doc-umask">
<h3><a class="doc-anchor" href="#doc-umask">#</a> <code>umask [mask]</code></h3>
<p>Set/show file-creation mask.</p>
<pre><code>umask 077 # owner-only by default</code></pre>
</article>
<article class="doc-entry" id="doc-times">
<h3><a class="doc-anchor" href="#doc-times">#</a> <code>times</code></h3>
<p>Print user + system times for the shell and its children.</p>
<pre><code>times</code></pre>
</article>
<article class="doc-entry" id="doc-caller">
<h3><a class="doc-anchor" href="#doc-caller">#</a> <code>caller</code></h3>
<p>Print the line + filename of the caller of the current function. Useful in error reporters and debuggers.</p>
<pre><code>my_assert() {
[[ $1 -eq $2 ]] || { echo "assertion failed at $(caller)"; exit 1; }
}</code></pre>
</article>
<article class="doc-entry" id="doc-help">
<h3><a class="doc-anchor" href="#doc-help">#</a> <code>help [name]</code></h3>
<p>Print help for a builtin (or list builtins).</p>
<pre><code>help echo
help</code></pre>
</article>
<article class="doc-entry" id="doc-enable-disable">
<h3><a class="doc-anchor" href="#doc-enable-disable">#</a> <code>enable</code> / <code>disable</code></h3>
<p>Enable/disable a builtin by name. Useful for testing external-vs-builtin behavior.</p>
<pre><code>disable cat # falls back to /bin/cat
enable cat # restore zshrs's cat builtin</code></pre>
</article>
<article class="doc-entry" id="doc-noglob">
<h3><a class="doc-anchor" href="#doc-noglob">#</a> <code>noglob cmd</code></h3>
<p>Run cmd with glob expansion disabled for its arguments. Equivalent to <code>setopt noglob; cmd; unsetopt noglob</code> as a one-liner.</p>
<pre><code>noglob find . -name '*.rs' # don't pre-glob *.rs</code></pre>
</article>
<article class="doc-entry" id="doc-ttyctl">
<h3><a class="doc-anchor" href="#doc-ttyctl">#</a> <code>ttyctl -f</code> / <code>ttyctl -u</code></h3>
<p>Freeze / unfreeze the tty state. Used by completion code that reads stdin.</p>
<pre><code>ttyctl -f # freeze
ttyctl -u # unfreeze</code></pre>
</article>
<article class="doc-entry" id="doc-sync">
<h3><a class="doc-anchor" href="#doc-sync">#</a> <code>sync</code></h3>
<p>Force the OS to flush its buffer cache to disk. Direct <code>sync(2)</code> syscall.</p>
<pre><code>cp big.iso /mnt/usb/
sync</code></pre>
</article>
<article class="doc-entry" id="doc-zmodload">
<h3><a class="doc-anchor" href="#doc-zmodload">#</a> <code>zmodload [-i] mod</code></h3>
<p>Load a zsh module. zshrs's modules are statically linked, so <code>zmodload</code> is a no-op-and-succeed for compat (<code>-i</code> idempotent flag).</p>
<pre><code>zmodload zsh/datetime
zmodload zsh/regex</code></pre>
</article>
<article class="doc-entry" id="doc-zsleep">
<h3><a class="doc-anchor" href="#doc-zsleep">#</a> <code>zsleep n</code></h3>
<p>Subsecond-precision sleep. <code>zsleep 0.5</code> sleeps 500ms. Different from coreutils <code>sleep</code> only in supporting fractional seconds reliably across implementations.</p>
<pre><code>zsleep 0.25 # 250ms</code></pre>
</article>
<article class="doc-entry" id="doc-zsystem">
<h3><a class="doc-anchor" href="#doc-zsystem">#</a> <code>zsystem subcmd</code></h3>
<p>zsh/system module operations. <code>zsystem flock file</code> for file locking; <code>zsystem getflags</code> for flag dumps.</p>
<pre><code>zsystem flock /tmp/lockfile</code></pre>
</article>
<article class="doc-entry" id="doc-strftime">
<h3><a class="doc-anchor" href="#doc-strftime">#</a> <code>strftime fmt [time]</code></h3>
<p>Format a unix timestamp using <code>strftime(3)</code> directives. Default time is <em>now</em>.</p>
<pre><code>strftime "%Y-%m-%d %H:%M:%S"</code></pre>
</article>
<article class="doc-entry" id="doc-mkdir">
<h3><a class="doc-anchor" href="#doc-mkdir">#</a> <code>mkdir [-p] dirs…</code></h3>
<p>Create directories. <code>-p</code> creates parents and doesn't fail if exists.</p>
<pre><code>mkdir -p ~/.config/myapp/{cache,logs}</code></pre>
</article>
<article class="doc-entry" id="doc-vared">
<h3><a class="doc-anchor" href="#doc-vared">#</a> <code>vared VAR</code></h3>
<p>Edit a variable's value via the line editor. Handy for interactive config edits.</p>
<pre><code>vared PATH</code></pre>
</article>
<article class="doc-entry" id="doc-zformat">
<h3><a class="doc-anchor" href="#doc-zformat">#</a> <code>zformat -f var fmt args…</code></h3>
<p>Sprintf with <code>%X:value</code> argument bindings; common in completion display formatting.</p>
<pre><code>zformat -f line "%name (%size)" name:foo size:42</code></pre>
</article>
<article class="doc-entry" id="doc-zregexparse">
<h3><a class="doc-anchor" href="#doc-zregexparse">#</a> <code>zregexparse</code></h3>
<p>State-machine regex parser used by completion and filename rewriting.</p>
<pre><code>zregexparse vname tname regex</code></pre>
</article>
<article class="doc-entry" id="doc-zcompile">
<h3><a class="doc-anchor" href="#doc-zcompile">#</a> <code>zcompile file</code></h3>
<p>Pre-compile a zsh script to <code>.zwc</code>. zshrs treats <code>.zwc</code> as one of its bytecode-cache inputs and deserializes the chunk directly when sourcing the original file.</p>
<pre><code>zcompile ~/.zshrc</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-job-control">
<h2>Job Control & Background</h2>
<p class="chapter-meta">11 topics · <code>cmd &</code> compiles to a sub-chunk + <code>BUILTIN_RUN_BG</code> (id 290) fork dispatch; full job-table integration is Phase G6.</p>
<article class="doc-entry" id="doc-amp">
<h3><a class="doc-anchor" href="#doc-amp">#</a> <code>cmd &</code> — background</h3>
<p>Run cmd in the background. The compile path detects <code>ListOp::Amp</code>, compiles cmd into a sub-chunk, emits <code>CallBuiltin(BUILTIN_RUN_BG, 1)</code>. The handler forks; child <code>setsid</code>s and runs the chunk on a fresh VM; parent returns Status(0) immediately.</p>
<pre><code>sleep 30 &
make all & make docs & wait</code></pre>
</article>
<article class="doc-entry" id="doc-jobs">
<h3><a class="doc-anchor" href="#doc-jobs">#</a> <code>jobs</code></h3>
<p>List background jobs known to the shell. Note: pids spawned via <code>BUILTIN_RUN_BG</code> aren't yet registered in the JobTable (Phase G6); externally-launched bg cmds via <code>std::process::Command::spawn()</code> are.</p>
<pre><code>jobs # active jobs
jobs -l # with pids</code></pre>
</article>
<article class="doc-entry" id="doc-fg">
<h3><a class="doc-anchor" href="#doc-fg">#</a> <code>fg [%jobspec]</code></h3>
<p>Bring a background/stopped job to the foreground. <code>%1</code>, <code>%2</code> select by job number; <code>%name</code> by command-name prefix.</p>
<pre><code>fg %1
fg %vim</code></pre>
</article>
<article class="doc-entry" id="doc-bg">
<h3><a class="doc-anchor" href="#doc-bg">#</a> <code>bg [%jobspec]</code></h3>
<p>Resume a stopped job in the background.</p>
<pre><code>bg %1</code></pre>
</article>
<article class="doc-entry" id="doc-kill">
<h3><a class="doc-anchor" href="#doc-kill">#</a> <code>kill [-SIG] pid|%job</code></h3>
<p>Send a signal. Default is <code>SIGTERM</code>. Builtin form supports both PID and jobspec.</p>
<pre><code>kill -INT 12345
kill %1
kill -l # list signals</code></pre>
</article>
<article class="doc-entry" id="doc-wait">
<h3><a class="doc-anchor" href="#doc-wait">#</a> <code>wait [pid|%job]</code></h3>
<p>Wait for a child to exit; with no args, waits for all bg children. Status is the wait-on'd child's exit code.</p>
<pre><code>worker1.sh & pid1=$!
worker2.sh & pid2=$!
wait $pid1 $pid2</code></pre>
</article>
<article class="doc-entry" id="doc-disown">
<h3><a class="doc-anchor" href="#doc-disown">#</a> <code>disown [%job]</code></h3>
<p>Remove a job from the job table without killing it. The process keeps running but is no longer tracked.</p>
<pre><code>long_task &
disown %1</code></pre>
</article>
<article class="doc-entry" id="doc-suspend">
<h3><a class="doc-anchor" href="#doc-suspend">#</a> <code>suspend</code></h3>
<p>Stop the current shell (sends <code>SIGTSTP</code> to itself). Common idiom in nested shells.</p>
<pre><code>suspend</code></pre>
</article>
<article class="doc-entry" id="doc-async">
<h3><a class="doc-anchor" href="#doc-async">#</a> <code>async 'cmd'</code></h3>
<p>Ship work to the worker pool and return an opaque ID. Unlike <code>&</code>, this runs on a thread within the same process — no fork, no setsid.</p>
<pre><code>id=$(async 'sleep 5; curl https://api.example.com')</code></pre>
</article>
<article class="doc-entry" id="doc-await">
<h3><a class="doc-anchor" href="#doc-await">#</a> <code>await ID</code></h3>
<p>Wait for an async task by ID and capture its stdout.</p>
<pre><code>id=$(async 'expensive-job')
result=$(await $id)</code></pre>
</article>
<article class="doc-entry" id="doc-bang-pid">
<h3><a class="doc-anchor" href="#doc-bang-pid">#</a> <code>$!</code> — last bg pid</h3>
<p>PID of the most-recently-backgrounded command. Used to wait/signal it later.</p>
<pre><code>long_task &
echo "spawned $!"
wait $!</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-coreutils">
<h2>Anti-Fork Coreutils</h2>
<p class="chapter-meta">23 topics · <strong>2000-5000x faster</strong> than forking to /bin — every invocation runs in-process via direct syscalls.</p>
<article class="doc-entry" id="doc-cu-cat">
<h3><a class="doc-anchor" href="#doc-cu-cat">#</a> <code>cat [files…]</code></h3>
<p>Concatenate files to stdout. With no args, copies stdin. <code>-n</code> numbers lines; <code>-A</code> shows non-printable. No fork.</p>
<pre><code>cat /etc/hosts
cat *.log | grep ERROR</code></pre>
</article>
<article class="doc-entry" id="doc-cu-head">
<h3><a class="doc-anchor" href="#doc-cu-head">#</a> <code>head [-n N] [files…]</code></h3>
<p>First N lines (default 10). <code>-c N</code> first N bytes.</p>
<pre><code>head -20 /var/log/system.log</code></pre>
</article>
<article class="doc-entry" id="doc-cu-tail">
<h3><a class="doc-anchor" href="#doc-cu-tail">#</a> <code>tail [-n N] [-f] [files…]</code></h3>
<p>Last N lines. <code>-f</code> follow (in-process).</p>
<pre><code>tail -f access.log</code></pre>
</article>
<article class="doc-entry" id="doc-cu-wc">
<h3><a class="doc-anchor" href="#doc-cu-wc">#</a> <code>wc [-lwc] [files…]</code></h3>
<p>Line / word / char count.</p>
<pre><code>wc -l *.rs # total Rust LOC</code></pre>
</article>
<article class="doc-entry" id="doc-cu-sort">
<h3><a class="doc-anchor" href="#doc-cu-sort">#</a> <code>sort [-n -r -u -k] [files…]</code></h3>
<p>Sort lines. <code>-n</code> numeric, <code>-r</code> reverse, <code>-u</code> unique, <code>-k N</code> sort on key.</p>
<pre><code>du -h * | sort -hr</code></pre>
</article>
<article class="doc-entry" id="doc-cu-find">
<h3><a class="doc-anchor" href="#doc-cu-find">#</a> <code>find path [predicates]</code></h3>
<p>Walk directories applying predicates. <code>-name</code>, <code>-type</code>, <code>-size</code>, <code>-mtime</code>, <code>-exec</code>. In-process walk.</p>
<pre><code>find . -name '*.rs' -size +1k</code></pre>
</article>
<article class="doc-entry" id="doc-cu-uniq">
<h3><a class="doc-anchor" href="#doc-cu-uniq">#</a> <code>uniq [-c -d -u] [files…]</code></h3>
<p>Filter adjacent duplicates. <code>-c</code> prefix counts, <code>-d</code> show duplicates only.</p>
<pre><code>cat history.log | sort | uniq -c | sort -nr</code></pre>
</article>
<article class="doc-entry" id="doc-cu-cut">
<h3><a class="doc-anchor" href="#doc-cu-cut">#</a> <code>cut [-d delim] -f fields</code></h3>
<p>Extract fields from each line.</p>
<pre><code>cut -d: -f1 /etc/passwd # all usernames</code></pre>
</article>
<article class="doc-entry" id="doc-cu-tr">
<h3><a class="doc-anchor" href="#doc-cu-tr">#</a> <code>tr SET1 SET2</code></h3>
<p>Translate or delete characters.</p>
<pre><code>echo HELLO | tr A-Z a-z
tr -d '\r' < crlf.txt > lf.txt</code></pre>
</article>
<article class="doc-entry" id="doc-cu-seq">
<h3><a class="doc-anchor" href="#doc-cu-seq">#</a> <code>seq [start] [step] end</code></h3>
<p>Print a sequence of numbers.</p>
<pre><code>for i in $(seq 1 10); do …; done
seq 0 0.5 5</code></pre>
</article>
<article class="doc-entry" id="doc-cu-rev">
<h3><a class="doc-anchor" href="#doc-cu-rev">#</a> <code>rev</code></h3>
<p>Reverse each line character-wise.</p>
<pre><code>echo hello | rev # olleh</code></pre>
</article>
<article class="doc-entry" id="doc-cu-tee">
<h3><a class="doc-anchor" href="#doc-cu-tee">#</a> <code>tee [-a] file…</code></h3>
<p>Copy stdin to stdout AND to one or more files. <code>-a</code> appends.</p>
<pre><code>build.sh 2>&1 | tee build.log</code></pre>
</article>
<article class="doc-entry" id="doc-cu-basename">
<h3><a class="doc-anchor" href="#doc-cu-basename">#</a> <code>basename path [suffix]</code></h3>
<p>Strip directory and optionally a trailing suffix.</p>
<pre><code>basename /tmp/file.txt # file.txt
basename /tmp/file.txt .txt # file</code></pre>
</article>
<article class="doc-entry" id="doc-cu-dirname">
<h3><a class="doc-anchor" href="#doc-cu-dirname">#</a> <code>dirname path</code></h3>
<p>Strip the trailing component.</p>
<pre><code>dirname /tmp/file.txt # /tmp</code></pre>
</article>
<article class="doc-entry" id="doc-cu-touch">
<h3><a class="doc-anchor" href="#doc-cu-touch">#</a> <code>touch [-a -m -t time] file…</code></h3>
<p>Update file timestamps; create empty file if missing.</p>
<pre><code>touch deploy.lock</code></pre>
</article>
<article class="doc-entry" id="doc-cu-realpath">
<h3><a class="doc-anchor" href="#doc-cu-realpath">#</a> <code>realpath path</code></h3>
<p>Resolve symlinks and relative paths to a canonical absolute path.</p>
<pre><code>realpath ./src/exec.rs</code></pre>
</article>
<article class="doc-entry" id="doc-cu-sleep">
<h3><a class="doc-anchor" href="#doc-cu-sleep">#</a> <code>sleep seconds</code></h3>
<p>Pause for <em>seconds</em> (fractional supported).</p>
<pre><code>sleep 0.5</code></pre>
</article>
<article class="doc-entry" id="doc-cu-whoami">
<h3><a class="doc-anchor" href="#doc-cu-whoami">#</a> <code>whoami</code></h3>
<p>Effective username (direct <code>geteuid</code> + <code>getpwuid</code> syscalls).</p>
<pre><code>echo "running as $(whoami)"</code></pre>
</article>
<article class="doc-entry" id="doc-cu-id">
<h3><a class="doc-anchor" href="#doc-cu-id">#</a> <code>id [-u -g -n] [user]</code></h3>
<p>User/group ID info.</p>
<pre><code>id -u # numeric uid
id -un # username</code></pre>
</article>
<article class="doc-entry" id="doc-cu-hostname">
<h3><a class="doc-anchor" href="#doc-cu-hostname">#</a> <code>hostname [-s -f]</code></h3>
<p>System hostname. Direct syscall.</p>
<pre><code>hostname # short
hostname -f # FQDN</code></pre>
</article>
<article class="doc-entry" id="doc-cu-uname">
<h3><a class="doc-anchor" href="#doc-cu-uname">#</a> <code>uname [-a -s -r -m]</code></h3>
<p>Kernel name / release / arch. Direct <code>uname(3)</code>.</p>
<pre><code>uname -srm</code></pre>
</article>
<article class="doc-entry" id="doc-cu-date">
<h3><a class="doc-anchor" href="#doc-cu-date">#</a> <code>date [+fmt]</code></h3>
<p>Print current date/time. <code>+fmt</code> uses <code>strftime</code> directives. Direct syscall.</p>
<pre><code>date
date +%s
date "+%Y-%m-%d %H:%M:%S"</code></pre>
</article>
<article class="doc-entry" id="doc-cu-mktemp">
<h3><a class="doc-anchor" href="#doc-cu-mktemp">#</a> <code>mktemp [-d] [template]</code></h3>
<p>Atomic temp file/dir creation.</p>
<pre><code>tmp=$(mktemp)
tmpdir=$(mktemp -d)</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-parallel">
<h2>Parallel Primitives</h2>
<p class="chapter-meta">5 topics · all run on the persistent worker pool, not via fork. Bytecode-VM execution under <code>rayon</code> task scheduler.</p>
<article class="doc-entry" id="doc-pmap">
<h3><a class="doc-anchor" href="#doc-pmap">#</a> <code>pmap 'cmd {}' args…</code></h3>
<p>Parallel map across worker pool. Output preserves input order. The <code>{}</code> placeholder gets each arg.</p>
<pre><code>pmap 'gzip {}' *.log
pmap 'sha256sum {}' **/*.iso</code></pre>
</article>
<article class="doc-entry" id="doc-pgrep">
<h3><a class="doc-anchor" href="#doc-pgrep">#</a> <code>pgrep 'pred {}' args…</code></h3>
<p>Parallel filter. Predicate runs concurrently; matching args are collected in input order.</p>
<pre><code>pgrep 'grep -q TODO {}' **/*.rs # files containing TODO</code></pre>
</article>
<article class="doc-entry" id="doc-peach">
<h3><a class="doc-anchor" href="#doc-peach">#</a> <code>peach 'cmd {}' args…</code></h3>
<p>Parallel for-each, unordered. Fire each task and don't preserve order — useful when output order doesn't matter.</p>
<pre><code>peach 'convert {} {}.png' *.svg</code></pre>
</article>
<article class="doc-entry" id="doc-barrier">
<h3><a class="doc-anchor" href="#doc-barrier">#</a> <code>barrier 'cmd1' ::: 'cmd2' ::: 'cmd3'</code></h3>
<p>Run all commands in parallel; wait for all to complete. Final status is the worst (highest) of all child statuses.</p>
<pre><code>barrier 'cargo test' ::: 'cargo doc' ::: 'cargo audit'</code></pre>
</article>
<article class="doc-entry" id="doc-barrier-fail-fast">
<h3><a class="doc-anchor" href="#doc-barrier-fail-fast">#</a> <code>barrier --fail-fast …</code></h3>
<p>Cancel remaining tasks as soon as one fails. Useful for guarded fan-out (<em>compile, test, lint</em>) where one failure makes the rest moot.</p>
<pre><code>barrier --fail-fast 'rustfmt --check src/' ::: 'cargo clippy'</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-aop">
<h2>AOP Intercept</h2>
<p class="chapter-meta">3 topics · the first shell with aspect-oriented programming. <code>before</code>/<code>after</code>/<code>around</code> advice. Routes through <code>host_exec_external</code> → <code>run_intercepts</code>.</p>
<article class="doc-entry" id="doc-intercept-before">
<h3><a class="doc-anchor" href="#doc-intercept-before">#</a> <code>intercept before pat { code }</code></h3>
<p>Run <em>code</em> before any command matching <em>pat</em>. <code>$INTERCEPT_NAME</code>, <code>$INTERCEPT_ARGS</code>, <code>$INTERCEPT_CMD</code> are bound. Pattern is glob: <code>git</code>, <code>git*</code>, <code>_*</code>.</p>
<pre><code>intercept before git { echo "[$(date)] $INTERCEPT_CMD" >> ~/git.log }</code></pre>
</article>
<article class="doc-entry" id="doc-intercept-after">
<h3><a class="doc-anchor" href="#doc-intercept-after">#</a> <code>intercept after pat { code }</code></h3>
<p>Run after the matched command finishes. Adds <code>$INTERCEPT_MS</code> (elapsed) and <code>$INTERCEPT_STATUS</code>.</p>
<pre><code>intercept after '_*' {
echo "$INTERCEPT_NAME took ${INTERCEPT_MS}ms"
}</code></pre>
</article>
<article class="doc-entry" id="doc-intercept-around">
<h3><a class="doc-anchor" href="#doc-intercept-around">#</a> <code>intercept around pat { code }</code> + <code>intercept_proceed</code></h3>
<p>Wrap the command. Call <code>intercept_proceed</code> from inside to invoke the original; skip it to suppress the original entirely. Replaces <code>defer</code>, <code>profile</code>, <code>memo</code>, <code>retry</code>, <code>timeout</code> with one primitive.</p>
<pre><code>intercept around expensive_func {
local cache=/tmp/cache_${INTERCEPT_ARGS// /_}
if [[ -f $cache ]]; then
cat $cache
else
intercept_proceed | tee $cache
fi
}</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-diagnostics">
<h2>Diagnostics & Profiling</h2>
<p class="chapter-meta">5 topics · zshrs-exclusive observability builtins.</p>
<article class="doc-entry" id="doc-doctor">
<h3><a class="doc-anchor" href="#doc-doctor">#</a> <code>doctor</code></h3>
<p>Full diagnostic dump: worker-pool metrics, SQLite cache stats, bytecode coverage, registered intercepts, history db size, options table.</p>
<pre><code>doctor</code></pre>
</article>
<article class="doc-entry" id="doc-dbview">
<h3><a class="doc-anchor" href="#doc-dbview">#</a> <code>dbview [table] [filter]</code></h3>
<p>Browse SQLite caches without writing SQL. Lists tables + row counts; with arguments shows specific entries.</p>
<pre><code>dbview # tables + row counts
dbview autoloads _git # one autoload entry
dbview comps git # completion search
dbview history docker # history search</code></pre>
</article>
<article class="doc-entry" id="doc-profile">
<h3><a class="doc-anchor" href="#doc-profile">#</a> <code>profile cmd</code></h3>
<p>In-process profiling with nanosecond accuracy. No fork, no <code>strace</code>. Reports CPU time, syscalls, allocations.</p>
<pre><code>profile cargo build</code></pre>
</article>
<article class="doc-entry" id="doc-zprof">
<h3><a class="doc-anchor" href="#doc-zprof">#</a> <code>zprof</code></h3>
<p>Function-level profile (zsh module compat). Enable with <code>zmodload zsh/zprof</code>; print accumulated stats with <code>zprof</code>.</p>
<pre><code>zmodload zsh/zprof
source ~/.zshrc
zprof | head -20</code></pre>
</article>
<article class="doc-entry" id="doc-history">
<h3><a class="doc-anchor" href="#doc-history">#</a> <code>history [-n N] [pattern]</code></h3>
<p>Browse command history. Backed by SQLite + FTS5 — full-text search, frequency-ranked, with timestamps and exit status per command.</p>
<pre><code>history -10 # last 10
history docker # FTS search</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-completion">
<h2>Completion System</h2>
<p class="chapter-meta">10 topics · compsys backed by SQLite FTS5. <code>compinit</code> pre-warms the autoload index across the worker pool.</p>
<article class="doc-entry" id="doc-compinit">
<h3><a class="doc-anchor" href="#doc-compinit">#</a> <code>compinit</code></h3>
<p>Initialize the completion system. zshrs's <code>compinit</code> is async: fpath is scanned + bytecode-compiled in the background while the prompt drops.</p>
<pre><code>autoload -Uz compinit && compinit</code></pre>
</article>
<article class="doc-entry" id="doc-compdef">
<h3><a class="doc-anchor" href="#doc-compdef">#</a> <code>compdef func cmd</code></h3>
<p>Bind a completion function to a command name.</p>
<pre><code>compdef _docker docker
compdef _kubectl kubectl k</code></pre>
</article>
<article class="doc-entry" id="doc-compadd">
<h3><a class="doc-anchor" href="#doc-compadd">#</a> <code>compadd [-J group] words…</code></h3>
<p>Add candidate completions. <code>-J</code> groups them; <code>-d desc</code> attaches descriptions.</p>
<pre><code>compadd -J commands start stop status restart</code></pre>
</article>
<article class="doc-entry" id="doc-compset">
<h3><a class="doc-anchor" href="#doc-compset">#</a> <code>compset</code></h3>
<p>Manipulate the completion state — peel off prefixes, set the position, etc.</p>
<pre><code>compset -P 'http://'
compset -S '*.com'</code></pre>
</article>
<article class="doc-entry" id="doc-compopt">
<h3><a class="doc-anchor" href="#doc-compopt">#</a> <code>compopt</code></h3>
<p>Toggle completion options for the current invocation.</p>
<pre><code>compopt -o nospace</code></pre>
</article>
<article class="doc-entry" id="doc-compgen">
<h3><a class="doc-anchor" href="#doc-compgen">#</a> <code>compgen [opts] word</code></h3>
<p>Bash-compat completion candidate generator.</p>
<pre><code>compgen -c git # all commands starting with "git"</code></pre>
</article>
<article class="doc-entry" id="doc-complete">
<h3><a class="doc-anchor" href="#doc-complete">#</a> <code>complete</code></h3>
<p>Bash-compat completion definition.</p>
<pre><code>complete -W "start stop status" myservice</code></pre>
</article>
<article class="doc-entry" id="doc-zstyle">
<h3><a class="doc-anchor" href="#doc-zstyle">#</a> <code>zstyle</code></h3>
<p>Style configuration for completion + prompt. zsh's universal config DSL.</p>
<pre><code>zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'</code></pre>
</article>
<article class="doc-entry" id="doc-cdreplay">
<h3><a class="doc-anchor" href="#doc-cdreplay">#</a> <code>cdreplay</code></h3>
<p>Replay deferred <code>compdef</code> calls accumulated during async <code>compinit</code>.</p>
<pre><code>compinit; cdreplay -q</code></pre>
</article>
<article class="doc-entry" id="doc-promptinit">
<h3><a class="doc-anchor" href="#doc-promptinit">#</a> <code>promptinit</code> + <code>prompt</code></h3>
<p>Theme system for prompts.</p>
<pre><code>autoload -Uz promptinit && promptinit
prompt redhat</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-zle">
<h2>ZLE Line Editor</h2>
<p class="chapter-meta">4 topics · 26 ZLE widgets, syntax highlighting, autosuggestions, abbreviations.</p>
<article class="doc-entry" id="doc-zle">
<h3><a class="doc-anchor" href="#doc-zle">#</a> <code>zle widget [-N name handler]</code></h3>
<p>Manipulate the line editor. <code>-N name handler</code> defines a user widget; <code>zle name</code> invokes one.</p>
<pre><code>my-widget() { LBUFFER+=" --verbose"; }
zle -N my-widget
bindkey '^Xv' my-widget</code></pre>
</article>
<article class="doc-entry" id="doc-bindkey">
<h3><a class="doc-anchor" href="#doc-bindkey">#</a> <code>bindkey [seq widget]</code></h3>
<p>Bind a key sequence to a widget. <code>-M map</code> targets a specific keymap.</p>
<pre><code>bindkey '^R' history-incremental-search-backward
bindkey -M vicmd 'k' up-line-or-beginning-search</code></pre>
</article>
<article class="doc-entry" id="doc-vared-zle">
<h3><a class="doc-anchor" href="#doc-vared-zle">#</a> <code>vared VAR</code> (line-edit form)</h3>
<p>Use ZLE to edit a variable's value interactively.</p>
<pre><code>vared LS_COLORS</code></pre>
</article>
<article class="doc-entry" id="doc-abbrev">
<h3><a class="doc-anchor" href="#doc-abbrev">#</a> Abbreviations</h3>
<p>Fish-style abbreviations: type the abbrev + space, the full command expands inline. Configured via the <code>fish/abbrs.rs</code> backend.</p>
<pre><code>abbr gco='git checkout'
abbr -g G='| grep'</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-pcre">
<h2>PCRE & Regex</h2>
<p class="chapter-meta">4 topics · zsh/pcre module support; the <code>=~</code> operator inside <code>[[ … ]]</code> uses PCRE2.</p>
<article class="doc-entry" id="doc-pcre-compile">
<h3><a class="doc-anchor" href="#doc-pcre-compile">#</a> <code>pcre_compile pattern</code></h3>
<p>Compile a PCRE pattern; bound to <code>$ZPCRE_OP</code>.</p>
<pre><code>pcre_compile '^(\d+)\s+(\w+)$'</code></pre>
</article>
<article class="doc-entry" id="doc-pcre-match">
<h3><a class="doc-anchor" href="#doc-pcre-match">#</a> <code>pcre_match string</code></h3>
<p>Match against the most-recently compiled pattern. Captures land in <code>$match</code>, <code>$mbegin</code>, <code>$mend</code>.</p>
<pre><code>pcre_compile '(\w+)@(\w+\.\w+)'
if pcre_match "alice@example.com"; then
echo "user: ${match[1]}, domain: ${match[2]}"
fi</code></pre>
</article>
<article class="doc-entry" id="doc-pcre-study">
<h3><a class="doc-anchor" href="#doc-pcre-study">#</a> <code>pcre_study</code></h3>
<p>Optimize the compiled pattern for repeated use.</p>
<pre><code>pcre_compile '\b\d{4}-\d{2}-\d{2}\b'
pcre_study</code></pre>
</article>
<article class="doc-entry" id="doc-regex-tilde">
<h3><a class="doc-anchor" href="#doc-regex-tilde">#</a> <code>[[ string =~ pattern ]]</code></h3>
<p>POSIX/PCRE regex match inside the conditional. Routed through <code>Op::RegexMatch</code> (host method <code>regex_match</code>).</p>
<pre><code>if [[ $version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
major=${match[1]}; minor=${match[2]}; patch=${match[3]}
fi</code></pre>
</article>
</section>
<!-- ════════════════════════════════════════════════════════════════════ -->
<section class="tutorial-section" id="ch-recently-closed">
<h2>Recently Closed Compat Gaps (eighty-eighth-pass)</h2>
<p class="chapter-meta">46 constructs · all verified against <code>~/forkedRepos/zsh/Src/</code> via direct C-source ports. Each entry is pinned by at least one assertion in <code>tests/zshrs_shell.rs</code>; workspace-wide test hooks today: 4,849 (see header strip). These were the highest-impact gaps in <code>man zshall</code> coverage that remained after the seventy-seventh pass — most are param expansion, pattern matching, function scoping, and trap behavior.</p>
<h3>Glob & Pattern</h3>
<article class="doc-entry" id="doc-gap-glob-tilde-exclude">
<h3><a class="doc-anchor" href="#doc-gap-glob-tilde-exclude">#</a> <code>extendedglob</code> <code>~</code> exclusion at PATH level</h3>
<p>Direct port of <code>pattern.c</code> P_EXCLUDE: matches RHS as a pattern against each LHS candidate's basename + full path, not as a separate CWD glob.</p>
<pre><code>setopt extendedglob
echo /tmp/dir/*.txt~*README* # all .txt except README.txt</code></pre>
</article>
<article class="doc-entry" id="doc-gap-glob-caret-path">
<h3><a class="doc-anchor" href="#doc-gap-glob-caret-path">#</a> <code>/^pat</code> at any path component</h3>
<p>Negation operator now triggers in any path component, not just the leading word.</p>
<pre><code>setopt extendedglob
echo /tmp/dir/^skipme # everything in /tmp/dir except files matching skipme</code></pre>
</article>
<article class="doc-entry" id="doc-gap-glob-var-prefix">
<h3><a class="doc-anchor" href="#doc-gap-glob-var-prefix">#</a> <code>$D/*</code>, <code>$D/(a|b)</code> — glob meta after var ref</h3>
<p>New <code>BUILTIN_GLOB_EXPAND</code> (id 343): pops a scalar pattern, runs <code>expand_glob</code>, pushes Value::Array. Compile path detects glob meta in literal segments only (so <code>$?</code>/<code>$#</code>/etc don't trigger).</p>
<pre><code>D=/tmp/build
echo $D/* # globs after $D substitution
echo $D/(a|b) # alternation after $D substitution</code></pre>
</article>
<article class="doc-entry" id="doc-gap-glob-recursive-sort">
<h3><a class="doc-anchor" href="#doc-gap-glob-recursive-sort">#</a> <code>**/*</code> recursive sort by full path</h3>
<p>Recursive globs sort by full-path lex order (zsh's depth-first walk). Non-recursive globs keep basename-sorted output.</p>
<pre><code>echo /tmp/dir/**/* # f, sub, sub/g — not f, g, sub</code></pre>
</article>
<h3>Parameter Expansion</h3>
<article class="doc-entry" id="doc-gap-nested-strip">
<h3><a class="doc-anchor" href="#doc-gap-nested-strip">#</a> <code>${${a%.txt}#hel}</code> — outer strip on inner result</h3>
<p>Direct port of <code>subst.c</code> getarg machinery — same dispatch for inner and outer.</p>
<pre><code>a=hello.txt
echo "${${a%.txt}#hel}" # lo</code></pre>
</article>
<article class="doc-entry" id="doc-gap-nested-flag">
<h3><a class="doc-anchor" href="#doc-gap-nested-flag">#</a> <code>${(s. .)${(j. .)a}}</code> — outer flag on inner expansion</h3>
<p>Flag handler now recurses on leading <code>${</code> in <code>rest</code>; applies U/L/C/Split/Join/SplitWords/SplitLines to inner result.</p>
<pre><code>a=(a b c)
echo "${(s. .)${(j. .)a}}" # a b c (split-then-rejoin)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-nested-cmdsubst">
<h3><a class="doc-anchor" href="#doc-gap-nested-cmdsubst">#</a> <code>${(flags)$(...)}</code> — cmd-subst as flag operand</h3>
<p>Branch added for cmd-subst operands. <code>(P)</code> indirect honored: captured output is treated as a variable name and looked up.</p>
<pre><code>a=hi; echo "${(P)$(echo a)}" # hi (NOT "a")
echo "${(U)$(echo hello)}" # HELLO
echo "${(z)$(echo a b c)}" # a b c</code></pre>
</article>
<article class="doc-entry" id="doc-gap-nested-subscript">
<h3><a class="doc-anchor" href="#doc-gap-nested-subscript">#</a> <code>${(U)${(s. .)s}[N]}</code> — [N] subscript after inner expansion</h3>
<p>Splits the flag-applied joined-scalar back to parts, indexes, re-applies case-transform flags.</p>
<pre><code>s="x y z"
echo "${(U)${(s. .)s}[1]}" # X</code></pre>
</article>
<article class="doc-entry" id="doc-gap-pad-empty-s1">
<h3><a class="doc-anchor" href="#doc-gap-pad-empty-s1">#</a> <code>${(l:N::s2:)val}</code> — pad with empty s1 + s2 fallback</h3>
<p>Pad parser collects both strings; when s1 empty and s2 given, s2 acts as fill char.</p>
<pre><code>echo "${(l:5::0:)42}" # 00000 (zsh-faithful)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-bracket-pair-delim">
<h3><a class="doc-anchor" href="#doc-gap-bracket-pair-delim">#</a> <code>${(j[+])a}</code>, <code>${(s[|])s}</code> — bracket-pair flag delimiters</h3>
<p>zsh subst.c <code>get_strarg</code> accepts matched bracket pairs as flag delimiters: <code>[</code>/<code>]</code>, <code>{</code>/<code>}</code>, <code>(</code>/<code>)</code>, <code><</code>/<code>></code>. Both flag-parser sites updated.</p>
<pre><code>a=(a b c)
echo "${(j[+])a}" # a+b+c
echo "${(j<X>)a}" # aXbXc
echo "${(s[|])\"x|y|z\"}" # x y z</code></pre>
</article>
<article class="doc-entry" id="doc-gap-q-modifier">
<h3><a class="doc-anchor" href="#doc-gap-q-modifier">#</a> <code>:Q</code> modifier — backslash escape removal</h3>
<p><code>hist.c</code> remquote: strips paired <code>'</code>/<code>"</code> AND <code>\X</code> backslash escapes. Both <code>:Q</code> paths fixed.</p>
<pre><code>a="a\\ b"
echo ${a:Q} # a b</code></pre>
</article>
<article class="doc-entry" id="doc-gap-replace-backslash">
<h3><a class="doc-anchor" href="#doc-gap-replace-backslash">#</a> <code>${a//\X/repl}</code> — backslash unescape for non-meta chars</h3>
<p>Pattern pre-pass strips <code>\X</code> when X is not a glob meta. <code>\?</code>/<code>\*</code>/<code>\[</code>/<code>\]</code>/<code>\(</code>/<code>\)</code>/<code>\|</code>/<code>\\</code> still escape.</p>
<pre><code>a="x:y:z"
echo "${a//\:/-}" # x-y-z
echo "${a//\./X}" # works on dots too</code></pre>
</article>
<article class="doc-entry" id="doc-gap-sort-flags-dq">
<h3><a class="doc-anchor" href="#doc-gap-sort-flags-dq">#</a> <code>"${(o)a[@]}"</code>, <code>"${(O)a[@]}"</code>, <code>"${(n)a[@]}"</code> — sort flags survive DQ when [@] given</h3>
<p>Compile path encodes the at-subscript context through a new <code>\u{03}</code> sentinel in the flags string so the runtime DQ-stripper preserves array-only flags.</p>
<pre><code>a=(c a b)
echo "${(o)a[@]}" # a b c
echo "${(O)a[@]}" # c b a
a=(10 2 1 22)
echo "${(n)a[@]}" # 1 2 10 22</code></pre>
</article>
<article class="doc-entry" id="doc-gap-special-default">
<h3><a class="doc-anchor" href="#doc-gap-special-default">#</a> <code>${SECONDS-default}</code> — zsh-special params always-set</h3>
<p>Whitelist treats <code>SECONDS</code>, <code>EPOCHSECONDS</code>, <code>EPOCHREALTIME</code>, <code>RANDOM</code>, <code>LINENO</code>, <code>HISTCMD</code>, <code>PPID</code>, <code>UID</code>, <code>EUID</code>, <code>GID</code>, <code>EGID</code>, <code>SHLVL</code> as set even when not in <code>self.variables</code>. <code>HISTCMD</code> getter also added.</p>
<pre><code>echo "${SECONDS-default}" # 0 (or current value)
echo "${UID-default}" # 501
echo "${HISTCMD-default}" # 0</code></pre>
</article>
<h3>Arrays & Subscripts</h3>
<article class="doc-entry" id="doc-gap-assoc-order">
<h3><a class="doc-anchor" href="#doc-gap-assoc-order">#</a> Associative-array key insertion order</h3>
<p>Storage switched from <code>HashMap</code> to <code>indexmap::IndexMap</code> so <code>${(k)h}</code> / <code>${(kv)h}</code> iterate in insertion order matching zsh's HashTable hnodes.</p>
<pre><code>typeset -A h
h=(a 1 b 2 c 3)
echo ${(k)h} # a b c
echo ${(kv)h} # a 1 b 2 c 3</code></pre>
</article>
<article class="doc-entry" id="doc-gap-multi-for">
<h3><a class="doc-anchor" href="#doc-gap-multi-for">#</a> <code>for k v in arr</code> — multi-name for loop</h3>
<p>Direct port of <code>parse.c par_for</code>: parser collects all leading identifiers; compiler emits N-stride iteration with empty-string fill on short tail.</p>
<pre><code>arr=(a 1 b 2 c 3)
for k v in $arr; do echo "$k=$v"; done
typeset -A h=(a 1 b 2)
for k v in ${(kv)h}; do echo "$k=$v"; done</code></pre>
</article>
<article class="doc-entry" id="doc-gap-array-array-splice">
<h3><a class="doc-anchor" href="#doc-gap-array-array-splice">#</a> <code>b=("${a[@]}")</code> — array-to-array splice preserves boundaries</h3>
<p>New <code>scalar_assign_depth</code> separate from <code>assign_context_depth</code> — only scalar RHS forces JOIN_STAR. Array init keeps splice.</p>
<pre><code>a=("1 2" "3 4")
b=("${a[@]}")
echo ${#b} # 2 (preserves both elements)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-bare-splice">
<h3><a class="doc-anchor" href="#doc-gap-bare-splice">#</a> <code>$a[@]</code>, <code>$a[*]</code> — bare (no-braces) splice</h3>
<p><code>array_splice_ref</code> extended to accept the no-braces form, identical semantics to <code>${a[@]}</code>.</p>
<pre><code>a=(x y z)
printf "%s\n" $a[@] # x, y, z (3 lines)
f() { echo $#; }; f $a[@] # 3</code></pre>
</article>
<article class="doc-entry" id="doc-gap-scalar-assign-join">
<h3><a class="doc-anchor" href="#doc-gap-scalar-assign-join">#</a> <code>b="${a[@]}"</code> — scalar assignment joins via JOIN_STAR</h3>
<p>Compile path forces <code>BUILTIN_ARRAY_JOIN_STAR</code> when <code>scalar_assign_depth > 0</code> (zsh subst.c forces single-string for scalar RHS).</p>
<pre><code>a=(1 2 3)
b="${a[@]}"
echo $b # 1 2 3</code></pre>
</article>
<article class="doc-entry" id="doc-gap-subscript-var-remove">
<h3><a class="doc-anchor" href="#doc-gap-subscript-var-remove">#</a> <code>a[$n]=()</code> — variable subscript element-remove</h3>
<p>Compile path now routes the subscript through <code>compile_word_str</code> when key has <code>$</code> or backtick.</p>
<pre><code>a=(1 2 3 4)
n=3
a[$n]=() # remove 3rd element
echo "${a[@]}" # 1 2 4
a=(1 2 3 4)
a[$#a]=() # remove last
echo "${a[@]}" # 1 2 3</code></pre>
</article>
<article class="doc-entry" id="doc-gap-assoc-i-flag">
<h3><a class="doc-anchor" href="#doc-gap-assoc-i-flag">#</a> <code>${h[(I)pat]}</code>, <code>${h[(R)pat]}</code> on assoc — return ALL matches</h3>
<p><code>(I)</code>/<code>(R)</code> return all matching keys/values space-joined; <code>(i)</code>/<code>(r)</code> return first. <code>(i)</code>/<code>(I)</code> search keys; <code>(r)</code>/<code>(R)</code> search values.</p>
<pre><code>typeset -A h=(a 1 b 2)
echo "${h[(I)*]}" # a b
echo "${h[(i)*]}" # a
echo "${h[(I)a]}" # a (single match)
typeset -A h=(a 1 b 1 c 2)
echo "${h[(R)1]}" # 1 1</code></pre>
</article>
<article class="doc-entry" id="doc-gap-typeset-preserve">
<h3><a class="doc-anchor" href="#doc-gap-typeset-preserve">#</a> <code>typeset -a a</code> preserves existing array at top scope</h3>
<p>Bare-declaration path now guards with <code>in_function || !exists</code>. <code>typeset -aU</code> dedupes in place.</p>
<pre><code>a=(1 2 3); typeset -a a; echo $a # 1 2 3 (was empty)
a=(a b a c b); typeset -aU a; echo $a # a b c (was empty)</code></pre>
</article>
<h3>Arithmetic</h3>
<article class="doc-entry" id="doc-gap-arith-subscript-rhs">
<h3><a class="doc-anchor" href="#doc-gap-arith-subscript-rhs">#</a> <code>((i=a[2]))</code> — RHS array subscript pre-resolve</h3>
<p><code>[</code> added to <code>needs_eval</code> trigger so MathEval (which runs <code>pre_resolve_array_subscripts</code>) handles it.</p>
<pre><code>a=(10 20 30)
((i=a[2])); echo $i # 20
((sum=a[1]+a[2]+a[3])); echo $sum # 60</code></pre>
</article>
<article class="doc-entry" id="doc-gap-arith-assoc-postinc">
<h3><a class="doc-anchor" href="#doc-gap-arith-assoc-postinc">#</a> <code>((h[a]++))</code>, <code>((h[a]+=v))</code> — compound ops on assoc</h3>
<p>Direct port of zsh's math.c LVAL_NUM_SUBSC: subscript receiver retains lvalue identity through compound operators.</p>
<pre><code>typeset -A h
h[a]=5
((h[a]++)) # h[a] = 6
((h[a]+=10)) # h[a] = 16</code></pre>
</article>
<article class="doc-entry" id="doc-gap-arith-pre-inc">
<h3><a class="doc-anchor" href="#doc-gap-arith-pre-inc">#</a> <code>((++a[i]))</code>, <code>((++h[k]))</code> — pre-increment on subscript</h3>
<p>New <code>parse_subscript_arith_pre_inc</code> + compile-side <code>subscripted_arith_compound_check</code> accepts the pre-op shape. Pre-op returns NEW value, post-op OLD.</p>
<pre><code>a=(10 20 30)
((++a[2])); echo $a # 10 21 30
typeset -A h
h[a]=5
((++h[a])); echo $h[a] # 6</code></pre>
</article>
<article class="doc-entry" id="doc-gap-arith-ternary">
<h3><a class="doc-anchor" href="#doc-gap-arith-ternary">#</a> <code>((a = cond ? T : F))</code> — ternary in assignment</h3>
<p><code>?</code> added to <code>needs_eval</code> triggers so MathEval handles ternary fully. ArithCompiler can't write back through <code>?:</code>.</p>
<pre><code>((a = 5 > 3 ? 99 : 0)); echo $a # 99</code></pre>
</article>
<article class="doc-entry" id="doc-gap-math-int-preserving">
<h3><a class="doc-anchor" href="#doc-gap-math-int-preserving">#</a> <code>abs</code>/<code>min</code>/<code>max</code>/<code>int</code>/<code>floor</code>/<code>ceil</code>/<code>trunc</code> — int-preserving</h3>
<p>Int-input return int (not <code>5.</code>). Float input still returns float.</p>
<pre><code>echo $((abs(-5))) # 5 (was 5.)
echo $((max(3,5,7))) # 7
echo $((abs(-5.5))) # 5.5 (still float)</code></pre>
</article>
<h3>Conditionals & Parser</h3>
<article class="doc-entry" id="doc-gap-cond-parens">
<h3><a class="doc-anchor" href="#doc-gap-cond-parens">#</a> <code>[[ a == a && (b == b || c == c) ]]</code> — cond grouping parens</h3>
<p>Lexer's <code>incondpat</code> resets on <code>&&</code>, <code>||</code>, <code>(</code>, <code>)</code>, <code>!</code>, <code>]]</code> per <code>cond.c par_cond_3</code>.</p>
<pre><code>[[ a == a && (b == b || c == c) ]] && echo y</code></pre>
</article>
<article class="doc-entry" id="doc-gap-case-paren-wrap">
<h3><a class="doc-anchor" href="#doc-gap-case-paren-wrap">#</a> <code>case W in (P|Q)) BODY ;;</code> — wrapped pattern with alternation</h3>
<p>Parser accepts both bare <code>(P) BODY</code> and wrapped <code>(P)) BODY</code> (the <code>(...)</code> is the pattern wrapper, the second <code>)</code> is the arm-close).</p>
<pre><code>case foo in
(foo|bar)) echo y;;
(*)) echo n;;
esac</code></pre>
</article>
<h3>Functions & Scoping</h3>
<article class="doc-entry" id="doc-gap-source-args">
<h3><a class="doc-anchor" href="#doc-gap-source-args">#</a> <code>. file.sh ARG1 ARG2</code> — source passes positionals</h3>
<p>Save outer <code>positional_params</code>, install <code>args[1..]</code> as new positionals, restore on exit.</p>
<pre><code># file.sh: echo "$1=$2"
. ./file.sh hi bye # hi=bye
set -- a b c
. ./file.sh inner # inside file.sh: $1=inner
echo "$@" # outside: a b c (preserved)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-local-assoc">
<h3><a class="doc-anchor" href="#doc-gap-local-assoc">#</a> <code>typeset -A h=(...)</code> in function shadows outer</h3>
<p>New <code>local_assoc_save_stack</code> mirrors <code>local_array_save_stack</code> lifecycle. Both <code>call_function</code> paths (legacy + bytecode) updated.</p>
<pre><code>typeset -A h=(a 1)
f() { typeset -A h=(b 2); echo $h[b]; }
f # 2
echo $h[a] # 1 (outer preserved)
echo "[${h[b]-empty}]" # [empty] (inner didn't leak)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-subshell-umask">
<h3><a class="doc-anchor" href="#doc-gap-subshell-umask">#</a> Subshell umask snapshot+restore</h3>
<p><code>SubshellSnapshot</code> snapshots <code>libc::umask</code>; <code>subshell_end</code> restores. zsh forks for <code>(...)</code> so umask dies with child; we run in-process so we manually reset.</p>
<pre><code>umask 022
(umask 077) # subshell
umask # 022 (parent unchanged)</code></pre>
</article>
<article class="doc-entry" id="doc-gap-subshell-exit-trap">
<h3><a class="doc-anchor" href="#doc-gap-subshell-exit-trap">#</a> Subshell EXIT trap fires at subshell end</h3>
<p>Was firing at process exit. Now fires before parent continues. <code>SubshellSnapshot</code> snapshots+restores parent traps; inner trap fires with <code>traps.remove("EXIT")</code> so the inner <code>execute_script</code> doesn't recurse.</p>
<pre><code>(trap "echo trapped" EXIT; true)
echo done # output: trapped\ndone (was: done\ntrapped)</code></pre>
</article>
<h3>Brace & Word Expansion</h3>
<article class="doc-entry" id="doc-gap-brace-with-var">
<h3><a class="doc-anchor" href="#doc-gap-brace-with-var">#</a> <code>{one,${a},three}</code> — brace expansion after var substitution</h3>
<p>Segment-concat path now emits <code>BUILTIN_BRACE_EXPAND</code> after concat when any literal segment contains <code>{</code> or <code>}</code>.</p>
<pre><code>a=hi
echo {one,${a},three} # one hi three
echo pre{1,${a},2}post # pre1post prehipost pre2post</code></pre>
</article>
<h3>Builtins & I/O</h3>
<article class="doc-entry" id="doc-gap-print-C">
<h3><a class="doc-anchor" href="#doc-gap-print-C">#</a> <code>print -C N</code> — column-padded output</h3>
<p>Per-column width padding with 2-space separator (zsh format). Trailing partial rows skip padding after the last present item.</p>
<pre><code>print -C 2 a b c d
# a c
# b d
print -C 2 alpha beta gamma delta
# alpha gamma
# beta delta</code></pre>
</article>
<article class="doc-entry" id="doc-gap-read-eE">
<h3><a class="doc-anchor" href="#doc-gap-read-eE">#</a> <code>read -e</code> / <code>read -E</code> — echo line</h3>
<p>zsh's <code>bin_read</code> calls fputs(buf, stdout) under both. <code>-e</code> echoes only (no assign); <code>-E</code> echoes AND assigns.</p>
<pre><code>echo "abc" | { read -E v; echo "[$v]"; }
# abc
# [abc]
echo "abc" | { read -e v; echo "[$v]"; }
# abc
# []</code></pre>
</article>
<article class="doc-entry" id="doc-gap-zerr-trap">
<h3><a class="doc-anchor" href="#doc-gap-zerr-trap">#</a> <code>trap "..." ZERR</code> / <code>trap "..." ERR</code> — fires on non-zero status</h3>
<p>Wired into <code>BUILTIN_ERREXIT_CHECK</code>. Runs the trap body before the errexit decision; <code>last_status</code> saved/restored to prevent recursion on trap-body failures.</p>
<pre><code>trap "echo zerr" ZERR
false
echo done
# zerr
# done</code></pre>
</article>
</section>
<p style="font-size: 11px; color: var(--text-muted); text-align: center; margin: 3rem 0 1rem; font-family: 'Share Tech Mono', monospace;">
// generated against fusevm 0.12.1 dispatch · zshrs v0.10.9 · 15 chapters · 4,849 #[test] hooks · MenkeTechnologies
</p>
</main>
</div>
<script src="hud-theme.js"></script>
</body>
</html>