Arrays 令人惊讶的阵列扩展行为

Arrays 令人惊讶的阵列扩展行为,arrays,bash,shell,ifs,Arrays,Bash,Shell,Ifs,我对以下示例中标记为(!!)的行感到惊讶: log1 () { echo $@; } log2 () { echo "$@"; } X=(a b) IFS='|' echo ${X[@]} # prints a b echo "${X[@]}" # prints a b echo ${X[*]} # prints a b echo "${X[*]}" # prints a|b echo "---" log1 ${X[@]} # prints a b log1 "${X[@]}"

我对以下示例中标记为
(!!)
的行感到惊讶:

log1 () { echo $@; }
log2 () { echo "$@"; }

X=(a b)
IFS='|'

echo ${X[@]}   # prints a b
echo "${X[@]}" # prints a b
echo ${X[*]}   # prints a b
echo "${X[*]}" # prints a|b
echo "---"
log1 ${X[@]}   # prints a b
log1 "${X[@]}" # prints a b
log1 ${X[*]}   # prints a b
log1 "${X[*]}" # prints a b (!!)
echo "---"
log2 ${X[@]}   # prints a b
log2 "${X[@]}" # prints a b
log2 ${X[*]}   # prints a b
log2 "${X[*]}" # prints a|b
以下是我对这种行为的理解:

  • ${X[*]}
    ${X[@]}
    都扩展到
    ab
  • “${X[*]}”
    扩展为
    “a | b”
  • “${X[@]}”
    扩展为
    “a”“b”
  • $*
    $@
    ${X[*]}
    ${X[@]}
    具有相同的行为,但它们的内容是程序或函数的参数
这一点似乎得到了政府的证实

因此,在
log1“${X[*]}”
行中,我希望引用的表达式扩展为“a | b”,然后传递给log1函数。该函数有一个显示的字符串参数。为什么会发生其他事情


如果你的答案有手动/标准参考的支持,那就太酷了

这是因为
$IFS
设置为
|

(X='a|b' ; IFS='|' ; echo $X)
输出:

a b
manbash
说:

如果是内部字段分隔符,用于扩展后的字拆分


在POSIX规范的[Special Parameters[()]部分中,我们可以找到

@

展开到位置参数,从一开始。展开发生在双引号内的时间和字段拆分的位置(请参见字段拆分)执行时,每个位置参数应扩展为单独的字段,前提是第一个参数的扩展仍应与原始单词的开头部分连接(假设扩展参数嵌入到单词中),最后一个参数的展开式仍应与原始单词的最后一部分连接。如果没有位置参数,@'的展开式将生成零个字段,即使“@”是双引号

*

展开到位置参数,从一开始。当展开发生在双引号字符串中时(请参见双引号),它应扩展到单个字段,每个参数的值由IFS变量的第一个字符分隔,或者如果IFS未设置,则由a分隔。如果IFS设置为空字符串,则这并不等同于取消设置它;它的第一个字符不存在,因此参数值是串联的

因此,从引用的变体开始(它们更简单):

我们看到,
*
扩展“将[s]扩展到单个字段,每个参数的值由IFS变量的第一个字符分隔”。这就是为什么从
echo“${X[*]”和
log2“${X[*}”中获得
a | b

我们还看到,
@
扩展扩展为“每个位置参数将扩展为一个单独的字段”。这就是为什么从
echo“${X[@]}”
log2“${X[@]}”获得
ab

你看到规范文本中关于字段拆分的注释了吗?“在哪里执行字段拆分(请参阅字段拆分)?这就是这里的神秘之处

除引号外,扩展的行为是相同的。不同之处在于之后会发生什么。特别是字段/字拆分

显示问题的最简单方法是在启用
set-x
的情况下运行代码

这让你明白:

+ X=(a b)
+ IFS='|'
+ echo a b
a b
+ echo a b
a b
+ echo a b
a b
+ echo 'a|b'
a|b
+ echo ---
---
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 'a|b'
+ echo a b
a b
+ echo ---
---
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 'a|b'
+ echo 'a|b'
a|b
这里需要注意的是,在调用
log1
时,除了最后一种情况,
|
已经消失了

它已经消失的原因是,没有引号,变量展开的结果(在本例中,
*
展开)是字段/字拆分。由于
如果使用来组合展开的字段,然后再次拆分,因此
会被字段拆分吞没


为了完成解释(对于实际存在问题的情况),即使调用中引用了扩展版本,
log1
(即
log1“${X[*]}”
,它正确地扩展为
log1“a | b”
)这是因为
log1
本身不使用
@
的引用扩展,因此函数中
@
的扩展本身就是分词(可以通过
echo a b
在该
log1
案例以及所有其他
log1
案例中看到)

  • “${X[*]}”
    按预期扩展为
    a | b
    ,因此
    $1
    log1
    中设置为
    a | b
  • 展开
    $@
    (无引号)时,生成的字符串是
    a | b
  • 不带引号的扩展将进行分词,并使用
    |
    作为分隔符(由于
    IFS
    的全局值),因此
    echo
    接收两个参数,
    a
    b

  • 哦,是的。有没有一种方法可以产生我想要的行为(即使用分隔符格式化,但不使用分词)?我认为
    printf
    可能是最简单的选择(例如:)始终使用带引号的扩展。这就是答案。您的
    log1
    函数根本不正确。
    log2
    是正确的形式。更准确地说,始终使用带引号的
    $@
    。(在某些情况下,您可能会故意将
    $*
    或其他参数保留为不带引号,但
    $@
    存在以供引用;在其他方面与
    $*
    相同)