我对以下示例中标记为(!!)
的行感到惊讶:
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[@]}
都展开到a b
"${X[*]}"
扩展为"a|b"
"${X[@]}"
扩展为"a" "b"
$*
和$@
与${X[*]}
和${X[@]}
具有相同的行为,但其内容是程序或功能的参数这似乎得到了bash manual的证实。
在行log1 "${X[*]}"
中,因此我希望引用的表达式扩展为“a | b”,然后传递给log1函数。该函数有一个显示的字符串参数。为什么会发生其他事情?
如果您的答案得到手动/标准参考的支持,那就太酷了!
答案 0 :(得分:7)
IFS
不仅用于加入${X[*]}
的元素,还用于拆分不带引号的扩展$@
。对于log1 "${X[*]}"
,会发生以下情况:
"${X[*]}"
按预期扩展为a|b
,因此$1
在a|b
内设置为log1
。$@
(未引用)时,生成的字符串为a|b
。|
作为分隔符进行分词(由于IFS
的全局值),因此echo
接收两个参数a
和b
。答案 1 :(得分:5)
这是因为$IFS
设置为|
:
(X='a|b' ; IFS='|' ; echo $X)
输出:
a b
man bash
说:
IFS内部字段分隔符,用于扩展后的单词拆分...
答案 2 :(得分:3)
在[特殊参数[(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02)的POSIX规范部分中,我们找到了。
@
从一个开始扩展到位置参数。当扩展发生在双引号内,并且执行字段拆分(参见字段拆分)时,每个位置参数应作为单独的字段扩展,并规定第一个参数的扩展仍应与开头部分连接原始单词(假设扩展参数嵌入在单词中),最后一个参数的扩展仍然与原始单词的最后部分连接。如果没有位置参数,则扩展' @'即使在' @'是双引号。
*
从一个开始扩展到位置参数。当扩展发生在双引号字符串中时(参见双引号),它将扩展为单个字段,每个参数的值由IFS变量的第一个字符分隔,或者由IFS取消设置。如果IFS设置为空字符串,则这不等于取消设置;它的第一个字符不存在,因此参数值是连接的。
首先从引用的变体开始(它们更简单):
我们看到*
扩展"将[s]扩展为单个字段,每个参数的值由IFS变量"的第一个字符分隔。这就是您从a|b
和echo "${X[*]"
获得log2 "${X[*]}"
的原因。
我们还看到@
扩展扩展为"每个位置参数将作为单独的字段扩展"。这就是您从a b
和echo "${X[@]}"
获得log2 "${X[@]}"
的原因。
您是否在规范文本中看到有关字段拆分的说明? "执行字段拆分(参见Field Splitting)&#34 ;?这就是这里神秘的关键。
在引号之外,扩展的行为是相同的。不同之处在于之后会发生什么。具体来说,字段/单词拆分。
显示问题的最简单方法是在启用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
被调用的时间|
已经消失。
它已经消失的原因是因为没有引号变量扩展的结果(在这种情况下,*
扩展)是字段/单词拆分。由于IFS
使用两者来组合要展开的字段,然后再将它们拆分,|
会被字段拆分吞噬。
要完成解释(对于实际存在问题的情况),即使调用中的扩展的引用版本(log1
扩展为{{log1 "${X[*]}"
,log1 "a|b"
失败的原因也是如此1}}正确)是因为log1
本身不使用@
的引用扩展,因此函数中@
的扩展本身是单词分割的( echo a b
案例中的log1
以及所有其他log1
案例都可以看到。