Process substitution, namerefs, and a shell you can embed
- #history
- #architecture
- #embedding
Some shell features exist for the person typing. Others exist for the person who has been writing shell for fifteen years and has stopped being reasonable about it. Mid-June was the second kind.
# huck, 10 June
$ printf 'diff <(echo same) <(echo same) && echo procsub-ok\n' | huck
huck: syntax error: expected a filename after redirection
$ printf 'declare -n ref=real; real=42; echo "$ref"\n' | huck
huck: declare: -n: not yet implemented in this version# huck, 23 June
$ printf 'diff <(echo same) <(echo same) && echo procsub-ok\n' | huck
procsub-ok
$ printf 'declare -n ref=real; real=42; echo "$ref"\n' | huck
42Process substitution is a small miracle of plumbing: <(echo same) runs a
command in the background, hands its output a filename — on Linux, something
under /dev/fd — and passes that to diff, which believes it is reading an
ordinary file. Namerefs (declare -n) are the shell's one concession to
indirection: a variable whose value is the name of another variable, so
assigning through it writes somewhere else. Neither is common. Both appear in
exactly the scripts you can't afford to break.
The other half: huck as a library
The bigger change that fortnight isn't visible from a prompt at all. huck was split into crates — a syntax front end, an interpreter, and the terminal program on top — so the interpreter could run with no terminal, no readline, and no assumptions about who's calling it:
let mut engine = Engine::new();
let out = engine.prepare("echo hello; echo world").capture();
assert_eq!(out.stdout, "hello\nworld\n");
assert_eq!(out.stderr, "");
assert_eq!(out.exit_code, 0);That opened up knobs an embedder needs and an interactive shell never thinks about: feed stdin from a byte buffer, merge stderr into stdout, set the working directory, run under a timeout, or run restricted — a sandbox mode where a script can't wander off into the filesystem.
Why do that six weeks into a shell? Because "can this run headless, from another program, with output captured" is a brutally effective test of architecture. Anything that secretly depended on a terminal, or wrote straight to the process's real stdout because that was convenient, gets exposed immediately. huck failed that test in a dozen places, and the fixes were the beginning of a much longer thread about where output goes — one that didn't finish until August.