Development record
2026-10-17 — Refines dcn-2610170726383253: a command where an unquoted ( stands in a word, glued to it or opening one after the command's name, is refused whole as one whose targets the line does not fix, whatever the command is, not only for the targets the guard reads. The words after the ( are not read as the command's own, so a flag there that makes it write (sed x(.) -i, cp a(.) -t dir) is not seen, and zsh may read the ( as glob qualifiers or modifiers that change the path a word names (echo hi(:A), ls x(/) are refused too). bash refuses every such line as a syntax error, so nothing a bash user runs is lost. A function definition's () after its name (f() { … }, f () { … }, function f() { … }) is not such a word and stays allowed, as does a quoted or escaped (.
2026-10-17 — Refines dcn-2610170726383253: a command where an unquoted ( stands in a word, glued to it or opening one after the command's name, is refused whole as one whose targets the line does not fix, whatever the command is, not only for the targets the guard reads. The words after the ( are not read as the command's own, so a flag there that makes it write (sed x(.) -i, cp a(.) -t dir) is not seen, and zsh may read the ( as glob qualifiers or modifiers that change the path a word names (echo hi(:A), ls x(/) are refused too). bash refuses every such line as a syntax error, so nothing a bash user runs is lost. A function definition's () after its name (f() { … }, f () { … }, function f() { … }) is not such a word and stays allowed, as does a quoted or escaped (.
Frontmatter
| id | dcn-2610170739186543 |
|---|---|
| type | decision |
| date | 2026-10-17 |
.abcd/work/decisions/dcn-2610170739186543-refines-dcn-2610170726383253-a-command.md
open on GitHub ↗ · commit history ↗
mentions2
- dcn-26101707263832532026-10-17 — Refines dcn-2610170704288549: the guard counts a { or } as opening or closing a group, for what a pipe into the group reaches, only where the line leaves that word wholly unquoted, the same test the approver's own reading of the text it hands back uses. A } with a quote in it, even an empty pair (}"" or }''), is a command's name to the shell, so the commands after it still read the group's piped input, and a shell among them is refused as one reading a stream (echo 'touch ran' | { }""; bash; } is refused, as the same line with true in place of the } is). And a removal or a write in a command where an unquoted ( stands in a word, glued to it (build/l(:A), x(.), a/b(/), a redirection's target) or opening one after the command's name, is refused as a target the line does not fix, whatever follows the (. zsh reads such a ( as glob qualifiers or modifiers that can change the path the word names: build/l(:A) is the real path a symlink leads to, outside the working tree; bash refuses the line. A find or an rsync in such a command is read as removing, since the words after the ( are not read as its own. A quoted or escaped ( is text and stays allowed (rm -f 'a(b)').
- dcn-26101708054463552026-10-17 — Refines dcn-2610170739186543: an approved command runs under bash, because the guard reads every command in bash's grammar, so the command it judged is the command that runs. The approval hands back cd -- '<dir>' && exec /bin/bash --noprofile --norc -c '<command>', the command one single-quoted word, so the person's login shell, zsh or bash, only changes directory and, when that succeeds, starts bash, with its profile and rc files switched off. A construct only zsh gives a meaning to (glob qualifiers, (:A) modifiers, { … } always { … }) is a syntax error to bash, and bash runs nothing of that line. The check of the text handed back reads it with the guard's own parse and approves it only when it is exactly that change of directory joined by && to that one bash call, with the whole command as its last word and nothing else, refused as tree-group-broken otherwise. It no longer reads the command's own groups, ifs, loops and cases, since nothing in the single-quoted word can end it early: a } or a fi with nothing open is bash's syntax error inside the working tree, not a refusal. The refusal of a command where an unquoted ( stands in a word, and the reading of a quoted } as a command's name, stay as a second layer.