我有两个数组
$ 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 从当前源读取输入,直到一行仅包含单词(没有尾随空白)可见。到该点读取的所有行都将用作标准输入[...] 命令。
[…]
如果任何部分单词被引用,分隔符是删除引号的结果单词,并且此处文档中的行未展开。如果单词不加引号,此处文档的所有行都经过参数扩展、命令替换和算术扩展 [...]。
(强调我的)。
考虑在命令行中引用某些内容的主要原因:
- 告诉 shell 如何分割行并创建参数列表(两个参数
a b
与一个参数"a b"
); - 告诉 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