Here is another patch that was pending.

Bash Version: 5.2
Commit 87a6e89edc7cb1af057e2c15666df189595d305b (the current devel)

Description:

  When `shopt -s patsub_replacement' is turned on and the pattern
  string `pat' of ${var/$pat/"&"} is just an anchoring character `#'
  or `%', the internal escape of `&' (i.e., `\&') remains in the
  result of the parameter expansion.  Also, with the same condition,
  the unquoted & in ${var/$pat/&} remains a literal & where we expect
  it to be expanded to an empty string.

Repeat-By:

  When `pat' contains non-anchoring characters, the expected result of
  the expansions is obtained as

  $ bash-dev --norc
  $ shopt -s patsub_replacement
  $ v=1234 pat=2; echo "${v/$pat/<&>}, ${v/$pat/<\&>}, ${v/$pat/"<&>"}"
  1<2>34, 1<&>34, 1<&>34
  $ v=1234 pat=#1; echo "${v/$pat/<&>}, ${v/$pat/<\&>}, ${v/$pat/"<&>"}"
  <1>234, <&>234, <&>234

  Here, unquoted & in the replacement is expanded to the matched
  string as expected while quoted &'s are expanded to the literal `&'.
  However, only when pat=# or pat=%, the result becomes

  $ v=1234 pat=#; echo "${v/$pat/<&>}, ${v/$pat/<\&>}, ${v/$pat/"<&>"}"
  <&>1234, <\&>1234, <\&>1234

  where unquoted & does not become an empty string, and quoted &'s
  become \&.  In particular, with ${v/$pat/"<&>"}, a backslash
  originally not present is inserted in the expanded result.  I
  instead expect the following result for consistency with the other
  cases:

  <>1234, <&>1234, <&>1234

Fix:

  I attach a patch `r0031-fix-patsub.patch.txt'.

  I guess the original intent of the related code has been to disable
  patsub_replacement for ${var/#/...} and ${var/%/...} where the
  pattern `# + <empty string>' or `% + <empty string>' is explicitly
  specified (not through a parameter expansion $pat), but I would
  still think we should keep the special treatment of unquoted & for
  these cases as well when patsub_replacement is turned on for
  consistency with other cases.

--
Koichi
From 3ff6e8b3fcb131676a7c7542b25d48b5768c8ea7 Mon Sep 17 00:00:00 2001
From: Koichi Murase <myoga.mur...@gmail.com>
Date: Mon, 24 Jan 2022 22:31:51 +0900
Subject: [PATCH 1/3] fix ${v/#PAT/"REP"}: process patsub_replacement also for
 the special cases

---
 subst.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/subst.c b/subst.c
index 163f42c1..666127c8 100644
--- a/subst.c
+++ b/subst.c
@@ -8930,36 +8930,38 @@ pat_subst (string, pat, rep, mflags)
    *       STRING and return the result.
    *   3.  A null STRING with a matching pattern means to append REP to
    *       STRING and return the result.
-   * These don't understand or process `&' in the replacement string.
    */
   if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
     {
-      replen = STRLEN (rep);
+      if (mflags & MATCH_EXPREP)
+       rstr = strcreplace (rep, '&', "", 2);
+      else
+       rstr = rep;
+      rslen = STRLEN (rstr);
       l = STRLEN (string);
-      ret = (char *)xmalloc (replen + l + 2);
-      if (replen == 0)
+      ret = (char *)xmalloc (rslen + l + 2);
+      if (rslen == 0)
        strcpy (ret, string);
       else if (mtype == MATCH_BEG)
        {
-         strcpy (ret, rep);
-         strcpy (ret + replen, string);
+         strcpy (ret, rstr);
+         strcpy (ret + rslen, string);
        }
       else
        {
          strcpy (ret, string);
-         strcpy (ret + l, rep);
+         strcpy (ret + l, rstr);
        }
+      if (rstr != rep)
+       free (rstr);
       return (ret);
     }
   else if (*string == 0 && (match_pattern (string, pat, mtype, &s, &e) != 0))
     {
-      replen = STRLEN (rep);
-      ret = (char *)xmalloc (replen + 1);
-      if (replen == 0)
-       ret[0] = '\0';
+      if (mflags & MATCH_EXPREP)
+       return strcreplace (rep, '&', "", 2);
       else
-       strcpy (ret, rep);
-      return (ret);
+       return savestring (rep);
     }
 
   ret = (char *)xmalloc (rsize = 64);
-- 
2.36.1

Reply via email to