Bash ${foo//(/\\(}未启用extglobs

Bash ${foo//(/\\(}未启用extglobs,bash,shell,parameter-expansion,Bash,Shell,Parameter Expansion,我正在尝试使用参数展开对括号进行转义。尽管启用了extglob,但以下代码不起作用: #!/usr/bin/env bash shopt -s extglob foo='file(2)' foo=${foo//(/\\(} foo=${foo//)/\\)} printf '%s\n' "$foo" # Expected: file\(2\) # Actual: file(2\) 当我禁用extglob或显式转义左括号时,它会正确输出file\(2\),如下所示: foo=${

我正在尝试使用参数展开对括号进行转义。尽管启用了
extglob
,但以下代码不起作用:

#!/usr/bin/env bash

shopt -s extglob

foo='file(2)'
foo=${foo//(/\\(}
foo=${foo//)/\\)}

printf '%s\n' "$foo"

# Expected:  file\(2\)
# Actual:    file(2\)
当我禁用
extglob
或显式转义左括号时,它会正确输出
file\(2\)
,如下所示:

foo=${foo//\(/\\(}  

为什么
extglob
会导致这种情况?我在那里没有看到任何
extglob
模式。此外,右括号没有反斜杠也可以正常工作

在线测试,也可在本地使用:

GNU bash, version 4.3.30(1)-release (x86_64-unknown-linux-gnu)
GNU bash, version 4.4.18(1)-release (x86_64-unknown-linux-gnu)
GNU bash, version 5.0.0(2)-alpha (x86_64-pc-linux-gnu)

引用搜索字符串可防止将其解释为全局,从而引发以下问题:

shopt -s extglob
foo='file(2)'
foo=${foo//'('/'\('}
foo=${foo//')'/'\)'}
printf '%s\n' "$foo"

(引用替换项也可以避免将反斜杠加倍)。

这是由于bash中的优化而导致的错误

在替换模式时,首先在字符串中使用bash。如果没有,则执行任何搜索和替换都没有意义。它的方法是根据需要在其周围使用
*..*
来构造新模式:

/*如果模式与字符串中的任何位置不匹配,请继续并
马上短路。一个小的优化,节省了大量的时间
对strmatch不必要的调用(对于N个字符串,最多调用N个
字符),如果匹配不成功。要保留语义
在下面的子字符串匹配中,我们确保模式
`*'作为第一个和最后一个角色,如果需要,制作新的图案*/
/*XXX-如果我实现了具有特殊意义的“**”,请稍后检查此项,
因为这可能会导致开头或结尾出现“**”*/
len=STRLEN(pat);
if(pat[0]!='*'| |(pat[0]='*'&&pat[1]==LPAREN&&extended_glob)| | pat[len-1]!='*'))
{
int unescaped_反斜杠;
char*pp;
p=npat=(char*)xmalloc(len+3);
p1=pat;
if(*p1!='*'.| |(*p1=='*'&&p1[1]==LPAREN&&extended_glob))
*p++='*';
它试图与字符串匹配的模式最终是
*(*

开头的
*(
现在无意中被识别为extglob的开头,但在bash时,它将以字符串形式匹配模式:

 prest = PATSCAN (p + (*p == L('(')), pe, 0); /* ) */
  if (prest == 0)
    /* If PREST is 0, we failed to scan a valid pattern.  In this
       case, we just want to compare the two as strings. */
    return (STRCOMPARE (p - 1, pe, s, se));
这意味着,除非要进行替换的字符串是字面上的
*(*
),否则优化会无效地拒绝认为无事可做的字符串。当然,这也意味着它对
*(*
本身正常工作:

$ f='*(*'; echo "${f//(/\\(}"
*\(*
如果要伪造此优化,请检查源代码:

diff --git a/subst.c b/subst.c
index fc00cab0..f063f784 100644
--- a/subst.c
+++ b/subst.c
@@ -4517,8 +4517,6 @@ match_upattern (string, pat, mtype, sp, ep)
   c = strmatch (npat, string, FNMATCH_EXTFLAG | FNMATCH_IGNCASE);
   if (npat != pat)
     free (npat);
-  if (c == FNM_NOMATCH)
-    return (0);

   len = STRLEN (string);
   end = string + len;
然后它将在您的情况下正常工作:

$ ./bash -c 'f="my string(1) with (parens)"; echo "${f//(/\\(}"'
my string\(1) with \(parens) 

但是我不明白
如何被解释为glob,而
不是?
只在前面的
)的上下文中才有意义。也就是说,我需要深入分析解析器以给出准确的答案,而不是“不要那样做”一般规则。不过,一般规则足够了,因为它使任何/所有全局行为与手头的操作完全无关。所以这是一些未记录的解析器的事情?这非常令人困惑,因为我甚至不知道如何
可以解释为一个
extglob
,没有
+
@
等或没有右括号。如果它真的是一个bug,我不会感到惊讶,但要做出更确切的决定,需要在邮件列表上进行讨论和/或审查相关代码的实现/更改历史/等等@mickp:这是我的错误。调查研究成功了++难以置信的研究量,太棒了!正是我想要的。非常感谢!