为什么 POSIX 中没有提到“${1-"$var"}”(下面的选项 6)?

为什么 POSIX 中没有提到“${1-"$var"}”(下面的选项 6)?

我能找到的唯一参考规范中是这样的

最好包含这样的语句:“从括起来的“${”到匹配的“}”的字符不应受到双引号的影响”,类似于“$()”的语句。然而,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)表明,实际上,外部引号保护反斜杠,就像\nvs.中一样"\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 行为可以指出是正确的,并且讨论的一部分涉及确定什么是正确的并清楚地表达它。

相关内容