Bash 如何处理参数扩展的字符串替换部分中的引用?

Bash 如何处理参数扩展的字符串替换部分中的引用?

有什么一致的逻辑吗?

some-command "${somevariable//some pattern/'how does this get parsed?'}"

我在下面发布了一些结论和原始测试作为“答案”,但无论如何它们都不是完整的答案。 Bash 手册页似乎没有提及这个主题。

答案1

正如评论中所讨论的,这似乎在 Bash 版本之间发生了变化。我认为这是相关的变化bash-4.3-alpha变更日志):

Z Z。当使用模式替换单词扩展时,bash 现在通过删除引号来运行替换字符串,因为它允许该字符串中的引号充当转义字符。这不向后兼容,因此可以通过将 bash 兼容模式设置为 4.2 来禁用它。

以及shopt -s compat42(在线手册):

compat42
如果设置,bash 不会使用引号删除来处理模式替换词扩展中的替换字符串。

引用单引号的示例:

$ s=abc\'def; echo "'${s//\'/\'\\\'\'}'"
'abc'\''def'

$ shopt -s compat42
$ s=abc\'def; echo "'${s//\'/\'\\\'\'}'"
'abc\'\\'\'def'

$ bash --version | head -1
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

解决方法:将替换字符串放入变量中,然后在替换中使用引号:

$ shopt -s compat42
$ qq="'\''"; s=abc\'def; echo "'${s//\'/$qq}'";
'abc'\''def'
$ qq="'\''"; s=abc\'def; echo "'${s//\'/"$qq"}'";
'abc"'\''"def'

有趣的是,如果展开式是未引用的,那么在所有版本中,替换后引号都会被删除。那就是s=abc; echo ${s/b/""}版画ac。这当然不会发生在其他扩展中,例如s='a""c' ; echo ${s%x}outputs a""c

答案2

逆向工程的一般规则:

  • 报价必须耦合(完成)
  • 保留报价(包含在实际替换中)
  • 如果反斜杠出现在任意字母之前,则反斜杠将被保留
  • 如果反斜杠转义单引号,则反斜杠将被保留
  • 即使在单引号内,反斜杠反斜杠序列也会减少为一个反斜杠
  • 您无法转义单引号内的单引号
  • 参数扩展在单引号内的工作方式与在单引号外的工作方式相同
  • 如果使用反斜杠转义美元符号,则美元符号将按字面保留,反斜杠将被删除

并得出结论:

  • 绝对没有办法'\''通过参数扩展来生成文字序列作为替换。
  • 然而,生成文字序列"'\''"作为替换是非常容易的。

接下来是一些原始测试。

[vagrant@localhost ~]$ echo "$0"
-bash
[vagrant@localhost ~]$ echo "${0//a/x}"
-bxsh
[vagrant@localhost ~]$ echo "${0//a/some long string  with spaces}"
-bsome long string  with spacessh
[vagrant@localhost ~]$ echo "${0//a/"quoted string"}"
-b"quoted string"sh
[vagrant@localhost ~]$ echo "${0//a/"unfinished quote}"
> wat
> }"
-b"unfinished quote}"
wat
sh
[vagrant@localhost ~]$ echo "${0//a/\"escaped quote}"
-b"escaped quotesh
[vagrant@localhost ~]$ echo "${0//a/\\escaped escape}"
-b\escaped escapesh
[vagrant@localhost ~]$ echo "${0//a/\'escaped single quote}"
-b\'escaped single quotesh
[vagrant@localhost ~]$ echo "${0//a/''}"
-b''sh
[vagrant@localhost ~]$ echo "${0//a/''''}"
-b''''sh
[vagrant@localhost ~]$ echo "${0//a/'''}"
> a'b}c"d
-b'''}"
a'bshcd
[vagrant@localhost ~]$ echo "${0//a/'''}"
> w'x}y"z
-b'''}"
w'xshyz
[vagrant@localhost ~]$ echo "${0//a/'\'\\"a test'\'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/'\''\\"a test'\'}"
-b'\''\"a test'\'sh
[vagrant@localhost ~]$ echo "${0//a/'\''\\"a test'\$0'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/\\"a test'\$0'}"
> w}x"y
-b\"a test'$0'}"
wshxy
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\$0'}"
-b\"a test'$0'sh
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\\'$0'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\\$0'}"
-b\"a test'\-bash'sh
[vagrant@localhost ~]$ 

答案3

通过实验,我发现以下语法可以在 GNU bash 版本 5.0.11(1)-release (x86_64-pc-linux-gnu) 中将单引号转换为双引号...

text="aaa'bbb'ccc"; echo "${text//$"'"/$'"'}"

结果是...

aaa"bbb"ccc

请注意,单引号模式必须用双引号括起来,双引号替换也必须用单引号括起来。

有一个相关的shopt记录为...

extquote  
    If set, $'string' and $"string" quoting is performed within
    ${parameter} expansions  enclosed  in  double quotes.  This option
    is enabled by default.

相关内容