Skip to content

fix(interpreter): exec >file redirects the shell's own output - #2593

Merged
chaliy merged 1 commit into
mainfrom
claude/exec-redirect
Oct 7, 2026
Merged

chaliy merged 1 commit into
mainfrom
claude/exec-redirect

Conversation

@chaliy

@chaliy chaliy commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

exec >log 2>&1, exec >>log and exec 2>/dev/null now redirect the script's own output. Before, the fd table recorded them but only >&N lookups used it, so everything still printed to the caller.

  • After each top-level command, fd 1/2 entries route that command's output (route_exec_output). Streaming is paused for the redirected streams.
  • exec 3>&1 >log; echo x >&3 still reaches the caller through a passthrough buffer. exec 1>&3 3>&- restores.
  • $( ) and pipelines keep their own stdout. $(cmd 2>&1) still captures instead of writing to the log.
  • A failed exec >/bad/path leaves fd 1 alone and returns status 1.
  • exec {var}>f with var unset no longer clobbers fd 1. Allocating an fd into var is still not implemented and is noted in L-PROC-001.

Why

This is a roadmap step 1 fidelity gap from the Debian-oracle case exec-redirect-whole-script. exec > >(tee log) 2>&1-style logging at the top of a script is a common agent pattern. In bashkit it silently did nothing.

Before / After

Script Before After (= bash)
exec >/tmp/l 2>&1; echo one; ls /nope prints both prints nothing, both in /tmp/l
exec 2>/dev/null; ls /nope; echo rc=$? error shown rc=2 only
exec 3>&1 >/tmp/l; echo a; echo b >&3 a, b b (and a in the log)
exec >/tmp/l; x=$(echo cap); exec >&3; echo $x (n/a) cap

Proof: new spec exec-redirect-shell-output.test.sh (5 cases, bash-verified, run through both the library and the CLI) and new streaming test exec_redirected_output_is_not_streamed. bash_comparison passes, and the oracle case now matches.

Risk

Medium-low. The change only applies while exec has redirected fd 1/2. Output written before the exec in the same top-level command, and not streamed yet, also goes to the log (documented in L-PROC-001). The depth-50 nested $( ) stack need is unchanged.

Checklist

  • Failing spec first
  • cargo test -p bashkit --lib / --test integration / bash_comparison
  • clippy -D warnings, fmt, check-okf
  • knowledge: limitations L-PROC-001, log

https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA


Generated by Claude Code

`exec >log 2>&1` was recorded but only used by `>&N`, so output still
printed. Route fd 1/2 entries per top-level command, pause streaming for
redirected streams, pass `>&3` saved-original writes through, normalize
`exec 1>&3` restores, and keep `$(cmd 2>&1)` capturing. A failed open
leaves the fd alone; an unset `{var}` no longer falls back to fd 1.

Claude-Session: https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA
@chaliy chaliy self-assigned this Oct 7, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit b93a3b3 Commit Preview URL

Branch Preview URL
Oct 07 2026, 08:13 PM

@chaliy
chaliy merged commit 5b4cc69 into main Oct 7, 2026
44 checks passed
@chaliy
chaliy deleted the claude/exec-redirect branch October 7, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant