这个命令:
echo '$$foo=bar' | sed -E "s/(\$\$foo=).*/\1$(echo hello)/"
输出:
$$foo=bar
如果我将sed
's 引号更改为单引号并将命令括在双引号中:
echo '$$foo=bar' | sed -E 's/(\$\$foo=).*/\1'"$(echo hello)"'/'
它输出所需的结果:
$$foo=hello
所以我想问题出在扩展的正则表达式和引号上,除非我遗漏了一些明显的东西。
sed
在命令替换中连接单引号和双引号是一个好的做法(sed 's/foo/'"$(command)"'/'
)吗?如何使用
sed
扩展正则表达式转义双美元符号?
答案1
POSIX 有话要说$
关于基本正则表达式和扩展正则表达式的解释方式的差异:
基本正则表达式 (BRE):
当
<dollar-sign>
($
) 用作整个 BRE 的最后一个字符时,应为锚点。当用作子表达式的最后一个字符时,实现可以将 a<dollar-sign>
视为锚点。应将<dollar-sign>
表达式(或可选的子表达式)锚定到匹配字符串的末尾;可以<dollar-sign>
说匹配最后一个字符后面的字符串结尾。
扩展正则表达式 (ERE):
<dollar-sign>
括号表达式之外的( ) 会将其结尾的$
表达式或子表达式锚定到字符串的末尾;这样的表达式或子表达式只能匹配以字符串的最后一个字符结尾的序列。例如,EREef$
和在字符串 中(ef$)
匹配,但在字符串 中匹配失败,并且 ERE有效,但永远无法匹配,因为阻止表达式匹配以最后一个字符结尾。ef
abcdef
cdefab
e$f
f
e$
结论:在 BRE 中,$
字符与其自身匹配,除非它是表达式或子表达式的最后一个字符(在这种情况下,它将(子)表达式锚定到行尾)。在 ERE 中,$
角色始终锚定到行尾。
当你使用
sed -E "s/(\$\$foo=).*/\1$(echo hello)/"
你的 ERE (因为你使用-E
)是($$foo=).*
并且这个表达式将绝不e$f
匹配(上面的 POSIX 文本包含类似的示例)。
你的命令
sed "s/\$\$foo/\$\$hello/"
使用 BRE$$foo
来匹配文字字符串,$$foo
因为$
字符不在表达式的末尾。
$
要匹配扩展正则表达式中的单个字符,请使用\$
或[$]
。要转义双引号字符串中的 shell,请使用\\\$
(转义的反斜杠后跟转义的美元符号)或[\$]
,即
sed -E "s/(\\\$\\\$foo=).*/\1$(echo hello)/"
或者
sed -E "s/([\$][\$]foo=).*/\1$(echo hello)/"
(反斜杠 in\1
不需要转义,因为如果后跟美元符号、反引号、双引号、另一个反斜杠或换行符,则反斜杠仅在双引号字符串中充当转义字符;因此 是\1
字面意思,而不是转义的1
;参考这里)。
简短的答案:
您可以用单引号引用需要单引号的位(并将其与包含 shell 扩展的双引号字符串连接起来),或者对需要在单个双引号字符串中转义的内容进行转义。这是一个品味问题。我更关心在表达式中使用命令替换,因为这是一个代码注入漏洞,除非您完全控制插入的字符串。
\\\$
或[\$]
在双引号字符串中。\$
或[$]
在单引号字符串中。