我能找到的唯一参考规范中是这样的:
最好包含这样的语句:“从括起来的“${”到匹配的“}”的字符不应受到双引号的影响”,类似于“$()”的语句。然而,System V shell 中的历史实践阻止了这一点。
显而易见的问题是:“那个历史问题是什么”,更重要的是,它对我们今天有何影响?
在C.2.5 Parameters and Variables
该页面的标题下方有很多引用扩展的示例,似乎已竭尽全力列出所有这些示例,但是没有出现两次双引号的示例。
描述
该问题与这 6 个引用替代方案的输出有关:
$b
,"$b"
,${a-$b}
,${a-"$b"}
"${a-$b}"
和"${a-"$b"}"
前五个是由 POSIX 定义的,并且一些 shell 同意此类扩展的结果。请注意,某些值会根据 的值进行拆分$IFS
:
$ export a=abc; for sh in dash ksh93 bash; do $sh -c 'IFS="bl"; b=value
printf "<%s> " 1$b 2"$b" 3${a-$b} 4${a-"$b"} 5"${a-$b}" 6"${a-"$b"}";
echo'; done
<1va> <ue> <2value> <3a> <c> <4a> <c> <5abc> <6abc>
<1va> <ue> <2value> <3a> <c> <4a> <c> <5abc> <6abc>
<1va> <ue> <2value> <3a> <c> <4a> <c> <5abc> <6abc>
但是,"${1-"$var"}"
在规范中的某处定义了吗?
即:与重复双引号,外部和内部。
附加 1
正如相关信息一样,看看这个结果: 请注意,上一个代码与下一个代码的唯一区别是$a
未设置:
$ unset a; for sh in dash ksh93 bash; do $sh -c 'IFS="bl"; b=value
printf "<%s> " 1$b 2"$b" 3${a-$b} 4${a-"$b"} 5"${a-$b}" 6"${a-"$b"}";
echo'; done
<1va> <ue> <2value> <3va> <ue> <4value> <5value> <6value>
<1va> <ue> <2value> <3va> <ue> <4value> <5value> <6va> <ue>
<1va> <ue> <2value> <3va> <ue> <4value> <5value> <6value>
附加 2
在实际测试所有引用选项时,常见 shell 的行为方式相互冲突。
$ for sh in dash ksh93 bash; do $sh -c 'IFS="bl"; b=value
printf "<%s> " 1\n 2"\n" 3${a-\n} 4${a-"\n"} 5"${a-\n}" 6"${a-"\n"}";
echo'; done
<1n> <2\n> <3n> <4\n> <5\n> <6\n>
<1n> <2\n> <3n> <4\n> <5\n> <6n>
<1n> <2\n> <3n> <4\n> <5\n> <6n>
这还不包括 zsh!。
答案1
为什么
"${1-"$var"}"
POSIX中没有提到?
看起来根本不清楚在扩展内部和外部都有引号意味着什么,并且似乎有一个已批准的更改使其未定义。
但是,在扩展之外使用引号是正常的,这就是我们为防止单词(字段)拆分和通配符所做的事情。还提到了将它们放在扩展中:
部分2.2.3 双引号说
用双引号 (
""
) 括起来的字符应保留双引号内所有字符的字面值,但字符反引号、<dollar-sign> 和 <backslash> 除外,如下所示:
${
[...] 在从 括起来到匹配 的字符串中}
,偶数个未转义的双引号或单引号(如果有)应出现。
对于命令替换,它说:
带引号的字符串中的输入字符也包含在 和 之间
$(
,匹配)
不应受到双引号 [...]
参数扩展中没有提到这一点,因此这意味着里面的字符${...}
也受到外面引号的影响。
${foo-\n}
vs.的示例"${foo-\n}"
(您的情况 3 和 5)表明,实际上,外部引号保护反斜杠,就像\n
vs.中一样"\n"
。
但它似乎没有同时提到内部和外部引号,而且也不清楚应该做什么。
如果内部的字符受到外部引号的影响,那么一种可能的解释是第一个内部双引号结束引用,第二个重新启动它,whatever
然后取消引用。 Ksh 似乎相当一致地遵循这种解释,例如:
$ printf "<%s>\n" "${foo-"foo \n *"}"
<foo>
<n>
<file1.txt>
<file2.txt>
我不确定这是否非常有用,而且几乎没有其他 shell 可以做到这一点。 (至少从我最近尝试过的那些来看是这样。)
对于其他的,许多输出只是<foo \n *>
,就好像字符串被完整引用一样,就好像内部双引号只是强调字符应该被引用的事实。
然后 Bash 和 Dash 似乎以不一致的方式处理反斜杠,并且 print<foo n *>
似乎不符合任何我能想到的逻辑规则,其\n
行为就好像它没有被引用一样,而*
和 空格的行为就好像它们一样是。
如果你添加它会变得更加毛茸茸单身的引号。
目睹这种不一致,这个问题以前被讨论过也许并不奇怪,而且似乎有一个待定的更改会明确地使该未定义,相关部分是:
对于除了提供子字符串处理的四种类型之外的参数扩展,在从括起来的“${”到匹配的“}”的字符串中,发生扩展的双引号应保留所有字符的字面值,但字符双引号、反引号、 和 除外。如果字符串中出现任何未转义的双引号字符(嵌入式命令替换除外),则行为未指定。
和
Shell 实现在处理“${...}”内的未转义双引号字符方面存在很大差异(四个子字符串处理变体除外)。因此,该标准未指定行为。 [...] 处理“${...}”形式的差异导致了历史上的 System V shell、BSD 和 KornShell 之间的不一致,POSIX.1-2008 的 Shell and Utilities 卷中的文本是尝试在不破坏太多应用程序的情况下融合它们。
这似乎就是那些历史原因。
第一个读起来的方式,似乎意味着${foo-"bar"}
也,而不仅仅是"${foo-"bar"}"
,尽管前者似乎不太容易在 shell 之间给出不同的结果。
我没有找到 2010 年对此的讨论,但这里有一些 2016 年的消息,这些消息也涉及单引号的工作原理:
引用的 ${ } 是 shell 的[最毛茸茸的]部分之一,从某种意义上说是最可怕的,并且确实很难复制 - 所有这些都未指定或将有问题,这一点也不奇怪8(目前还无法理解)。
好的。 Geoff 的立场是,第 7 期文本是对应用程序的要求,而不是 shell,此外,该要求分别适用于单引号和双引号的意图(即,不能嵌套它们)的措辞含糊不清。
因此,在讨论解释 221 时,没有任何 shell 行为可以指出是正确的,并且讨论的一部分涉及确定什么是正确的并清楚地表达它。