Skip to content

shell: do not wrap an escaped value in double quotes - #24035

Merged
MikeMcQuaid merged 1 commit into
Homebrew:mainfrom
rawsun007:fix/shell-export-double-quotes
Sep 19, 2026
Merged

MikeMcQuaid merged 1 commit into
Homebrew:mainfrom
rawsun007:fix/shell-export-double-quotes

Conversation

@rawsun007

Copy link
Copy Markdown
Contributor

  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

Utils::Shell.sh_quote backslash-escapes every character outside its safe set, which is the right escaping for a value that stands on its own. Three call sites wrapped that output in double quotes, where a backslash before an ordinary character stays literal:

$ brew ruby -e 'require "utils/shell"; puts Utils::Shell.export_value("HOMEBREW_FOO", "/opt/home brew", :bash)'
export HOMEBREW_FOO="/opt/home\ brew"

$ bash --norc -c 'export HOMEBREW_FOO="/opt/home\ brew"; printf "%s\n" "$HOMEBREW_FOO"'
/opt/home\ brew

The backslash lands in the value. set_variable_in_profile, in the same file, already emits export VAR=<escaped> with no quotes, so the file disagreed with itself about the same value.

Feeding 23 values (spaces, quotes, $, backticks, newlines, tabs, non-ASCII, empty) through the real shells and reading the variable back: 18 of 23 come back wrong under bash, zsh, sh and ksh, and 19 of 23 under fish. With the quotes removed, all 23 round-trip exactly in all five.

A default install never sees it, because /opt/homebrew and /usr/local contain nothing sh_quote escapes. It bites where a path does — a prefix with a space in it, or an SDK under an Xcode <version>.app, which reaches CMAKE_INCLUDE_PATH and HOMEBREW_SDKROOT. The callers are brew --env --shell=... and the keg-only build-flag caveats.

Two "supports Bash" examples and two --env examples asserted the quoted shape with values sh_quote does not touch, so all four changed; the three new examples use values that tell the two forms apart. Reverting only utils/shell.rb fails exactly those five shell examples and one --env example.

Deliberately untouched: prepend_path_in_profile and set_variable_in_profile wrap the line in echo '...', so a value containing a single quote still builds a malformed command. Different fix, no caller passes one today; happy to send it separately.

AI disclosure: written with Claude Code (Claude Opus 5) on my account. The counts above come from a harness that ran each generated line through the installed bash, zsh, sh, ksh and fish and compared the resulting variable against the input. I answer review comments myself.

sh_quote backslash-escapes every unsafe character, which is only
correct outside quotes: inside double quotes the shell keeps the
backslash, so export_value turned "/opt/home brew" into the literal
/opt/home\ brew. set_variable_in_profile in the same file already
emits the unquoted form.

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Sep 19, 2026
Merged via the queue into Homebrew:main with commit 1bccf06 Sep 19, 2026
52 checks passed
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.

2 participants