我正在尝试使用参数扩展对括号进行转义。尽管如果启用了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\)
当我禁用file\(2\)
或显式转义左括号时,它会正确输出extglob
:
foo=${foo//\(/\\(}
为什么extglob
会导致这种情况?我在那里看不到任何extglob
模式。此外,右括号可以正常使用而无需反斜杠。
在tutorialspoint.com上进行了在线测试,并在本地使用:
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)
答案 0 :(得分:3)
由于bash的优化,这是一个错误。
替换图案时,先对字符串进行checks whether the pattern matches anywhere打击。如果不是,那么进行任何搜索和替换都没有意义。它的操作方式是通过在必要时用*..*
将其包围来构造新的模式:
/* If the pattern doesn't match anywhere in the string, go ahead and
short-circuit right away. A minor optimization, saves a bunch of
unnecessary calls to strmatch (up to N calls for a string of N
characters) if the match is unsuccessful. To preserve the semantics
of the substring matches below, we make sure that the pattern has
`*' as first and last character, making a new pattern if necessary. */
/* XXX - check this later if I ever implement `**' with special meaning,
since this will potentially result in `**' at the beginning or end */
len = STRLEN (pat);
if (pat[0] != '*' || (pat[0] == '*' && pat[1] == LPAREN && extended_glob) || pat[len - 1] != '*')
{
int unescaped_backslash;
char *pp;
p = npat = (char *)xmalloc (len + 3);
p1 = pat;
if (*p1 != '*' || (*p1 == '*' && p1[1] == LPAREN && extended_glob))
*p++ = '*';
它尝试与字符串匹配的模式最终为*(*
现在无意中将开头*(
视为extglob的开始,但是当bash fails to find the closing )
时,它以字符串形式匹配模式:
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)
答案 1 :(得分:2)
引用搜索字符串可以防止将其解释为问题,因此可以解决问题:
shopt -s extglob
foo='file(2)'
foo=${foo//'('/'\('}
foo=${foo//')'/'\)'}
printf '%s\n' "$foo"
(也要引用替换项,以避免将反斜杠加倍)。