Skip to content

fix(shellenv): preserve newlines in exported env values (#2814)#2919

Open
mikeland73 wants to merge 2 commits into
mainfrom
claude/focused-goldberg-1l9i51
Open

fix(shellenv): preserve newlines in exported env values (#2814)#2919
mikeland73 wants to merge 2 commits into
mainfrom
claude/focused-goldberg-1l9i51

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2814.

eval "$(devbox global shellenv)" printed bash: 1__bp_interactive_mode: ambiguous redirect at every prompt when the environment contained a multiline value (in the reporter's case a PROMPT_COMMAND built by bash-preexec / an elementary-terminal dbus hook).

Root cause

exportify (internal/devbox/envvars.go) emits each value as export KEY="…"; and escapes special characters by prefixing them with \. It also escaped \n, so a newline in a value was written as a backslash + literal newline:

export PROMPT_COMMAND="… >/dev/null 2>&1\
__bp_interactive_mode";

Inside double quotes, a backslash-newline is a line continuation — the shell removes it entirely on eval. So the newline was silently deleted and the two lines were joined:

… >/dev/null 2>&1__bp_interactive_mode

2>&1__bp_interactive_mode makes the redirect target 1__bp_interactive_mode, which bash rejects as an ambiguous redirect — exactly the reported error.

Fix

A literal newline inside double quotes is preserved as-is and needs no escaping (per the POSIX quoting rules, only $, `, ", and \ are special there). So exportify now stops escaping \n and emits the newline literally, keeping multiline values intact. A regression test pins the behavior.

The change is limited to the POSIX-shell export path; exportifyNushell already did not escape newlines and is untouched.

How was it tested?

  • go test ./internal/devbox/ -run TestExportify — passes, including the new TestExportifyMultilineValue.

  • go vet ./internal/devbox/ and gofmt -l — clean.

  • End-to-end in real bash, reproducing the reporter's value. Before the fix the sourced value collapses to … 2>&1__bp_interactive_mode (deleted newlines → ambiguous redirect); after the fix the newlines are preserved:

    === OLD (backslash-newline, line continuation) ===
    VALUE=[echo start>/dev/null 2>&1__bp_interactive_mode]
    
    === NEW (literal newline) ===
    VALUE=[echo start
    >/dev/null 2>&1
    __bp_interactive_mode]
    

Community Contribution License

All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.

By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.


cc @proedie (issue reporter) — thanks for the precise diagnosis, including the exact line that needed moving.

🤖 Generated with Claude Code

https://claude.ai/code/session_016JemcBvhaZqahAzACV4aQF


Generated by Claude Code

exportify escaped a newline inside a double-quoted value as a
backslash-newline. In POSIX shells a backslash-newline is a line
continuation that is removed on eval, so any multiline env value had its
newlines silently deleted, joining adjacent lines.

For a multiline PROMPT_COMMAND this turned `... 2>&1\<newline>foo` into
`... 2>&1foo`, making the shell emit "ambiguous redirect" on every
prompt. A literal newline inside double quotes is preserved as-is and
needs no escaping, so stop escaping it.

Fixes #2814

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016JemcBvhaZqahAzACV4aQF
Copilot AI review requested due to automatic review settings July 12, 2026 14:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes incorrect POSIX-shell export generation for environment variables whose values contain literal newlines (e.g., multiline PROMPT_COMMAND). It adjusts exportify so newlines are emitted literally inside double quotes (instead of being turned into backslash-newline line continuations that eval removes), and adds a regression test to lock in the behavior.

Changes:

  • Stop escaping \n in internal/devbox/envvars.go POSIX-shell export generation to preserve multiline values.
  • Add TestExportifyMultilineValue to ensure multiline values round-trip without producing \\\n line continuations.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
internal/devbox/envvars.go Updates exportify escaping rules to preserve literal newlines inside double-quoted export values (fix for #2814).
internal/devbox/envvars_test.go Adds a regression test validating multiline values remain multiline and never contain backslash-newline.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The explanatory comment inside the rune loop widened the scope of the
single-letter loop variable `r`, tripping golangci-lint's varnamelen.
Move the comment above the loop so the loop body matches the original
structure; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016JemcBvhaZqahAzACV4aQF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Bash: ambiguous redirect on eval "$(devbox global shellenv)"

3 participants