Skip to content

fix(interpreter): exec inside a subshell redirects the subshell - #2615

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

chaliy merged 1 commit into
mainfrom
claude/subshell-exec-redirect

Conversation

@chaliy

@chaliy chaliy commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

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

Stacked on #2610 — review that one first; this PR's own commit is the second one.

What changed

An exec redirect set inside a subshell now applies to the subshell's output.
Output the subshell wrote before the exec still goes to the caller, as in
bash, and a write to a descriptor the subshell closed names the command that
tried.

Why

The redirect applied to nothing at all. Only the outermost command loop routed
the exec fd table, and a subshell saves and restores its own table, so by the
time control was back at that loop the subshell's redirect was already gone.

( exec > log.txt; echo inside )
echo sep
cat log.txt
output
bash 5.2 sep, inside
bashkit before inside, sep — and log.txt empty
bashkit after sep, inside

( exec 2>err.txt; ls /nope ) lost the capture the same way: the error went to
the caller's stderr and err.txt stayed empty.

Before / After

( echo pre; exec > log.txt; echo post ); echo sep; cat log.txt
output
bash 5.2 pre, sep, post
bashkit before pre, post, sep — log.txt empty
bashkit after pre, sep, post
( exec 1>&- ; echo x )

bash and bashkit now both say
bash: line 1: echo: write error: Bad file descriptor and exit 1.

Risk

Low, and contained to the subshell boundary. The routing helper gained a
"keep" floor so the split between what the caller sees and what the redirect
takes does not depend on whether a streaming callback is attached — the
in-process exec() path and the CLI now agree. Two interpreter fields track
where the enclosing sequence stood when exec installed its target, and the
name of the last simple command, for a diagnostic raised after that command
returned.

An exec redirect inside a bash -c child still does not take effect;
L-PROC-001 is updated to say so.

Checklist

  • cargo test -p bashkit --lib (2920 passed)
  • cargo test -p bashkit --test integration (1376 passed)
  • 3 new spec cases and 4 integration tests, verified against bash 5.2
  • bash_comparison differential tests against real bash
  • cargo clippy -p bashkit --all-targets -- -D warnings
  • cargo fmt --all --check
  • cargo test -p bashkit-cli (56 passed)
  • just check-okf
  • Stack probe: 1900 KB, inside the two-MiB TM-DOS-020 budget
  • Knowledge updated (log.md, limitations.md L-PROC-001)

https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA


Generated by Claude Code

@chaliy chaliy self-assigned this Oct 8, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 8, 2026 •

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 1a88f9c Commit Preview URL

Branch Preview URL
Oct 08 2026, 03:22 AM

Base automatically changed from claude/closed-fd-write to main October 8, 2026 01:20
@chaliy
chaliy force-pushed the claude/subshell-exec-redirect branch from e94a5fe to fb225e9 Compare October 8, 2026 01:50
`( exec > log.txt; echo inside )` wrote to the caller's stdout and left
log.txt empty. Only the outermost command loop routed the exec fd table,
and the subshell's table was restored before control reached it, so the
redirect applied to nothing. Routing now happens at the subshell
boundary; output written before the `exec` still goes to the caller.

Claude-Session: https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA
@chaliy
chaliy force-pushed the claude/subshell-exec-redirect branch from fb225e9 to 1a88f9c Compare October 8, 2026 03:21
@chaliy
chaliy merged commit 75cd5ae into main Oct 8, 2026
46 checks passed
@chaliy
chaliy deleted the claude/subshell-exec-redirect branch October 8, 2026 03:41
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