From 1b668f567567d9f549a5b0e1e39726d9e5fffbce Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sun, 3 Apr 2022 11:06:40 +0200 Subject: [PATCH] Don't use results of quoted command substitution in adjacent variable expansion Given set var a echo "$var$(echo b)" the double-quoted string is expanded right-to-left, so we construct an intermediate "$varb". Since the variable "varb" is undefined, this wrongly expands to the empty string (should be "ab"). Fix this by isolating the expanded command substitution internally. We do the same when handling unquoted command substitutions. Fixes #8849 --- CHANGELOG.rst | 1 + src/expand.cpp | 2 ++ tests/checks/cmdsub.fish | 3 +++ 3 files changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f593e1a9e..bbd92bd6e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,7 @@ Deprecations and removed features Scripting improvements ---------------------- +- Quoted command substitution that directly follow a variable expansion (like ``echo "$var$(echo x)"``) no longer affect the variable expansion (:issue:`8849`). - ``math`` can now handle underscores (``_``) as visual separators in numbers (:issue:`8611`, :issue:`8496`):: math 5 + 2_123_252 diff --git a/src/expand.cpp b/src/expand.cpp index 7a2aa30e7..49e588f84 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -758,7 +758,9 @@ static expand_result_t expand_cmdsubst(wcstring input, const operation_context_t whole_item.reserve(paren_begin + 1 + sub_res_joined.size() + 1 + tail_item.completion.size()); whole_item.append(input, 0, paren_begin - have_dollar); + whole_item.push_back(INTERNAL_SEPARATOR); whole_item.append(sub_res_joined); + whole_item.push_back(INTERNAL_SEPARATOR); whole_item.append(tail_item.completion.substr(const_strlen(L"\""))); if (!out->add(std::move(whole_item))) { return append_overflow_error(errors); diff --git a/tests/checks/cmdsub.fish b/tests/checks/cmdsub.fish index fca2b6b45..16ab9e92b 100644 --- a/tests/checks/cmdsub.fish +++ b/tests/checks/cmdsub.fish @@ -63,3 +63,6 @@ echo "($(echo A)B$(echo C))" echo "quoted1""quoted2"(echo unquoted3)"$(echo quoted4)_$(echo quoted5)" # CHECK: quoted1quoted2unquoted3quoted4_quoted5 + +var=a echo "$var$(echo b)" +# CHECK: ab