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
是正确的形式。更准确地说,始终使用带引号的$@
。(在某些情况下,您可能会故意将$*
或其他参数保留为不带引号,但$@
存在以供引用;在其他方面与$*
相同)