Bash sed 替换双美元符号 $$ 扩展正则表达式

sch*_*ity 4 sed regular-expression

这个命令:

echo '$$foo=bar' | sed -E "s/(\$\$foo=).*/\1$(echo hello)/"
Run Code Online (Sandbox Code Playgroud)

输出:

$$foo=bar
Run Code Online (Sandbox Code Playgroud)

如果我将sed's 引号更改为单引号并将命令括在双引号中:

echo '$$foo=bar' | sed -E 's/(\$\$foo=).*/\1'"$(echo hello)"'/'
Run Code Online (Sandbox Code Playgroud)

它输出所需的结果:

$$foo=hello
Run Code Online (Sandbox Code Playgroud)

所以我想问题出在扩展的正则表达式和引号上,除非我遗漏了一些明显的东西。

  1. sed在命令替换中连接单引号和双引号是一个好的做法( sed 's/foo/'"$(command)"'/')吗?

  2. 如何使用sed扩展正则表达式转义双美元符号?

Kus*_*nda 5

$POSIX对于基本正则表达式和扩展正则表达式的解释方式的差异有这样的说法:

基本正则表达式 (BRE):

当<dollar-sign>( $) 用作整个 BRE 的最后一个字符时,应为锚点。<dollar-sign>当用作子表达式的最后一个字符时,实现可以将 a视为锚点。应将<dollar-sign>表达式(或可选的子表达式)锚定到匹配字符串的末尾;可以<dollar-sign>说匹配最后一个字符后面的字符串结尾。

扩展正则表达式 (ERE):

括号表达式之外的( <dollar-sign>)$会将其结尾的表达式或子表达式锚定到字符串的末尾;这样的表达式或子表达式只能匹配以字符串的最后一个字符结尾的序列。例如,EREef$和在字符串 中(ef$)匹配,但在字符串 中匹配失败,并且 ERE有效,但永远无法匹配,因为阻止表达式匹配以最后一个字符结尾。efabcdefcdefabe$ffe$

结论:在 BRE 中,$字符与其自身匹配,除非它是表达式或子表达式的最后一个字符(在这种情况下,它将(子)表达式锚定到行尾)。在 ERE 中,$角色始终锚定到行尾。

当你使用

sed -E "s/(\$\$foo=).*/\1$(echo hello)/"
Run Code Online (Sandbox Code Playgroud)

你的 ERE (因为你使用-E)是($$foo=).*并且这个表达式永远不会匹配(上面的 POSIX 文本包含e$f类似的示例)。

你的命令

sed "s/\$\$foo/\$\$hello/"
Run Code Online (Sandbox Code Playgroud)

使用 BRE$$foo来匹配文字字符串$$foo,因为$字符不在表达式的末尾。

$要匹配扩展正则表达式中的单个字符,请使用\$或[$]。要转义双引号字符串中的 shell,请使用\\\$(转义的反斜杠后跟转义的美元符号)或[\$],即

sed -E "s/(\\\$\\\$foo=).*/\1$(echo hello)/"
Run Code Online (Sandbox Code Playgroud)

或者

sed -E "s/([\$][\$]foo=).*/\1$(echo hello)/"
Run Code Online (Sandbox Code Playgroud)

(反斜杠 in\1不需要转义,因为反斜杠仅在双引号字符串中充当转义字符(如果后跟美元符号、反引号、双引号、另一个反斜杠或换行符);因此是\1字面意思,而不是转义的1;引用这里)。

简短的答案:

  1. 您可以用单引号引用需要单引号的位(并将其与包含 shell 扩展的双引号字符串连接起来),或者对需要在单个双引号字符串中转义的内容进行转义。这是一个品味问题。我更关心在表达式中使用命令替换,因为这是一个代码注入漏洞,除非您完全控制插入的字符串。

  2. \\\$或[\$]在双引号字符串中。\$或[$]在单引号字符串中。