有什么一致的逻辑吗?
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.