From 4251edccc2e4fa587b26e908b301283cc94ae651 Mon Sep 17 00:00:00 2001 From: Agando Date: Tue, 27 Oct 2026 17:10:40 +0100 Subject: fix(newos4): `if test …` was always false and `for` items never globbed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by running the shipped runner the way a user would, not by re-reading it. - _evalTest() stripped [ ] but not the `test` keyword, so `if test -f /work/alpha.txt` evaluated the word "test" as an operator, returned false, and every such branch took the else arm — measured live: "alpha missing" for a file that exists. Both spellings work now (`test -f x`, `[ -f x ]`), plus `-d`, `-e`, `-z`, `-n`, `!` and the = / != comparisons. - `for f in /work/*.txt` ran ONCE with $f set to the literal pattern, because only command arguments went through _expandGlobs. Loop items are expanded per iteration now, so the body runs once per matched file with the real path. - _expandGlobs() falls back to readDir() when a fs has no ls(), so the helper works against either shape instead of silently returning the literal pattern. Verification, live on v4.6.119: - for+glob: "--- /work/alpha.txt / 3 /work/alpha.txt / --- /work/beta.txt / 1 /work/beta.txt" (6 statements, exit 0) — previously one iteration printing the pattern. - conditionals: "alpha exists", "nope missing", grep + $? intact. - end-to-end /work/report.sh (loop + glob + >> append + test -f) wrote a correct log: "start / file: /work/alpha.txt / 3 /work/alpha.txt / file: /work/beta.txt / 1 /work/beta.txt …". - unknown command still reports "command not found" and never calls the AI. - test-shell-script.js 27 -> 35 asserts. Retracted: I reported `echo x > file` as broken. It was my probe monkey-patching terminal.print, which bypassed the capture stack the redirect uses. Both paths write correctly: direct helper -> "direct-call", prompt -> "prompt-call\n". --- modules/ext/shell.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'modules/ext/shell.js') diff --git a/modules/ext/shell.js b/modules/ext/shell.js index ad66e63..5784ef3 100644 --- a/modules/ext/shell.js +++ b/modules/ext/shell.js @@ -721,7 +721,12 @@ class ShellModule { const dirPath = '/' + rawDir.map(s => String(s).split('/').filter(Boolean).join('/')) .filter(Boolean).join('/'); let entries = []; - try { entries = this.terminal.fs.ls(dirPath) || []; } catch (e) { entries = []; } + try { + const fsv = this.terminal.fs; + // ls() is the canonical lister; readDir() is the fallback so the helper also works + // against a bare fake fs (and against any future module that only implements one). + entries = (fsv && (fsv.ls ? fsv.ls(dirPath) : null)) || (fsv && fsv.readDir ? fsv.readDir(dirPath) : null) || []; + } catch (e) { entries = []; } const re = globToRe(namePat); const hits = entries.filter(e => re.test(e.name)).map(e => (dirPath === '/' ? '' : dirPath) + '/' + e.name); return hits.length ? hits : [a]; @@ -733,7 +738,11 @@ class ShellModule { // and the bracket spelling of the same. Anything else is false, never a crash. _evalTest(expr, env) { const t = this._expandVars(String(expr == null ? '' : expr), env || {}) - .trim().replace(/^\[/, '').replace(/\]$/, '').trim(); + .trim() + .replace(/^\[\s*/, '') // `[ -f x ]` + .replace(/\s*\]$/, '') + .replace(/^test\s+/, '') // `test -f x` — without this every `if test` was false + .trim(); if (!t) return false; const fs = this.terminal.fs; const parts = t.split(/\s+/); @@ -755,10 +764,17 @@ class ShellModule { let ran = 0; for (const node of nodes) { if (node.op === 'for') { - for (const item of node.items) { - env[node.var] = this._expandVars(item, env); - const r = await this._runInstructions(node.body, env, ctx); - ran += r.ran + 1; + for (const rawItem of node.items) { + const expanded = this._expandVars(rawItem, env); + // `for f in /work/*.txt` used to run ONCE with $f set to the literal pattern, + // because only command arguments went through glob expansion. + const cwd = (this.terminal.fs && this.terminal.fs.currentPath) || []; + const items = this._expandGlobs([expanded], cwd); + for (const item of items) { + env[node.var] = item; + const r = await this._runInstructions(node.body, env, ctx); + ran += r.ran + 1; + } } continue; } -- cgit v1.2.3