为什么这里文档中的双引号数组扩展在这里似乎不起作用?

为什么这里文档中的双引号数组扩展在这里似乎不起作用?

我有两个数组

$ arr1=( 1 2 2 3)
$ arr2=( 2 3 3 4)

当我遵循双引号数组扩展的良好实践时,为什么会生成奇怪的输出

$ tsort << EOF
> "${arr1[@]}" "${arr2[@]}"
> EOF
"1
"2
2
3
3"
4"

而当我不遵循良好实践时,这个会产生正确的结果?我该怎么办?谢谢。

$ tsort << EOF
${arr1[@]} ${arr2[@]} 
EOF

1
2
3
4

我认为这里的文档中仍然执行分词:

$ wc -l << EOF
a    
b  
1
EOF
3

答案1

来自bash手册:

[...] 这里文档的所有行都经过参数扩展、命令替换和算术扩展,字符序列 \newline 被忽略,并且\必须使用 ' ' 来引用字符 ' \'、' $' 和 ' `' 。

它没有说任何关于删除引号、字段分割或文件名扩展的内容,因此定界文档中的引号是按字面意思理解的,并且不执行其通常的功能。

答案2

另一个答案基本上是“因为手动的这么说”,但我认为有一些理由这种行为的背后。

这种类型的重定向指示 shell 从当前源读取输入,直到一行仅包含单词(没有尾随空白)可见。到该点读取的所有行都将用作标准输入[...] 命令。

[…]

如果任何部分单词被引用,分隔符是删除引号的结果单词,并且此处文档中的行未展开。如果单词不加引号,此处文档的所有行都经过参数扩展、命令替换和算术扩展 [...]。

(强调我的)。

考虑在命令行中引用某些内容的主要原因:

  1. 告诉 shell 如何分割行并创建参数列表(两个参数a b与一个参数"a b");
  2. 告诉 shell 是否应该执行扩展(''相对于""或不加引号)。

第一个原因在创建标准输入时不适用,因为标准输入是溪流并且其中没有参数数量的概念。第二个原因可能适用,但设计选择仅通过引用或不引用来控制扩展单词( "EOF"vs. EOF) 使其无效。

我该怎么办?

您应该记住,您在此处创建了一个流。使用引号的原因不会出现在其中的已失效。实际上,前提是你使用的每一个引用应该出现在流中。

如果这更符合您的预期,那么未转义的引号就会消失没有任何区别。我想这里的文档将支持\"\'表示应该保留的引用。但这些将是您真正需要使用的唯一报价。目前的情况让事情变得简单了。

答案3

让我们看看替换为 时实际发生的tsort情况cat

$ arr1=( 1 2 2 3 )
$ arr2=( 2 3 3 4 )
$ cat <<END
> "${arr1[@]}" "${arr2[@]}"
> END
"1 2 2 3" "2 3 3 4"

正如您所看到的,此处文档只不过是一个文本字符串,其中包含在其中扩展的数组值。双引号来自文档本身的双引号(shell 只关心该${...}位,不会触及引号字符)。

删除双引号时的输出将是相同的,但没有双引号,

1 2 2 3 2 3 3 4

这将被解释为tsort

1 2   <-- first two numbers from arr1
2 3   <-- last two numbers from arr1
2 3   <-- first two numbers from arr2
3 4   <-- last two numbers from arr2

不幸的是,您选择了这个特定的示例,因为这恰好与

1 2   <-- first number from arr1 and arr2
2 3   <-- second number from arr1 and arr2
2 3   <-- etc.
3 4

即,每个数组的条目分为两列(每个数组一列)。

要(正确)生成第二个列表,您不能真正使用此处文档。相反,您可以使用 shell 循环:

for (( i=0; i<${#arr1[@]}; ++i)); do
    printf '%d %d\n' "${arr1[i]}" "${arr2[i]}"
done | tsort

相关内容