令人惊讶的数组扩展行为
Surprising array expansion behaviour
我对以下示例中标记为 (!!)
的行感到惊讶:
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 函数。该函数有一个显示的字符串参数。为什么会发生其他事情?
如果您的回答得到 manual/standard 参考支持,那就太好了!
那是因为 $IFS
设置为 |
:
(X='a|b' ; IFS='|' ; echo $X)
输出:
a b
man bash
说:
IFS The Internal Field Separator that is used for word splitting after expansion ...
在[特殊参数][(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02) 的 POSIX 规范部分,我们发现。
@
Expands to the positional parameters, starting from one. When the expansion occurs within double-quotes, and where field splitting (see Field Splitting) is performed, each positional parameter shall expand as a separate field, with the provision that the expansion of the first parameter shall still be joined with the beginning part of the original word (assuming that the expanded parameter was embedded within a word), and the expansion of the last parameter shall still be joined with the last part of the original word. If there are no positional parameters, the expansion of '@' shall generate zero fields, even when '@' is double-quoted.
*
Expands to the positional parameters, starting from one. When the expansion occurs within a double-quoted string (see Double-Quotes), it shall expand to a single field with the value of each parameter separated by the first character of the IFS variable, or by a if IFS is unset. If IFS is set to a null string, this is not equivalent to unsetting it; its first character does not exist, so the parameter values are concatenated.
所以从引用的变体开始(它们更简单):
我们看到 *
扩展 "expand[s] to a single field with the value of each parameter separated by the first character of the IFS variable"。这就是为什么你从 echo "${X[*]"
和 log2 "${X[*]}"
.
得到 a|b
我们还看到 @
扩展扩展到 "each positional parameter shall expand as a separate field"。这就是为什么你从 echo "${X[@]}"
和 log2 "${X[@]}"
.
得到 a b
您是否看到规范文本中有关字段拆分的注释? "where field splitting (see Field Splitting) is performed"?这就是这里的谜团的关键。
除了引号外,扩展的行为是相同的。区别在于之后会发生什么。具体来说,field/word分裂。
显示问题的最简单方法是 运行 您的代码启用 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
被调用时,除了最后一个案例 |
已经消失 .
它已经消失的原因是因为没有引号,变量扩展的 结果 (在本例中是 *
扩展)被 field/word 拆分.并且由于 IFS
用于 both 来组合正在扩展的字段然后再次拆分它们 |
被字段拆分吞没了。
为了完成解释(对于实际有问题的情况),即使在调用中引用了扩展的版本(即扩展为 log1 "${X[*]}"
,log1
失败的原因log1 "a|b"
正确)是因为 log1
本身 没有使用 @
的引用扩展,所以函数中 @
的扩展是本身分词(正如 echo a b
在 log1
案例以及所有其他 log1
案例中所见)。
IFS
不仅用于连接 ${X[*]}
的元素,还用于拆分未引用的扩展 $@
。对于 log1 "${X[*]}"
,会发生以下情况:
"${X[*]}"
按预期扩展为 a|b
,因此 </code> 在 <code>log1
. 中设置为 a|b
- 展开
$@
(未加引号)时,结果字符串为 a|b
.
- 未加引号的扩展以
|
为分隔符进行分词(由于IFS
的全局值),因此echo
接收两个参数,a
和 b
.
我对以下示例中标记为 (!!)
的行感到惊讶:
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 函数。该函数有一个显示的字符串参数。为什么会发生其他事情?
如果您的回答得到 manual/standard 参考支持,那就太好了!
那是因为 $IFS
设置为 |
:
(X='a|b' ; IFS='|' ; echo $X)
输出:
a b
man bash
说:
IFS The Internal Field Separator that is used for word splitting after expansion ...
在[特殊参数][(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02) 的 POSIX 规范部分,我们发现。
@
Expands to the positional parameters, starting from one. When the expansion occurs within double-quotes, and where field splitting (see Field Splitting) is performed, each positional parameter shall expand as a separate field, with the provision that the expansion of the first parameter shall still be joined with the beginning part of the original word (assuming that the expanded parameter was embedded within a word), and the expansion of the last parameter shall still be joined with the last part of the original word. If there are no positional parameters, the expansion of '@' shall generate zero fields, even when '@' is double-quoted.
*
Expands to the positional parameters, starting from one. When the expansion occurs within a double-quoted string (see Double-Quotes), it shall expand to a single field with the value of each parameter separated by the first character of the IFS variable, or by a if IFS is unset. If IFS is set to a null string, this is not equivalent to unsetting it; its first character does not exist, so the parameter values are concatenated.
所以从引用的变体开始(它们更简单):
我们看到 *
扩展 "expand[s] to a single field with the value of each parameter separated by the first character of the IFS variable"。这就是为什么你从 echo "${X[*]"
和 log2 "${X[*]}"
.
a|b
我们还看到 @
扩展扩展到 "each positional parameter shall expand as a separate field"。这就是为什么你从 echo "${X[@]}"
和 log2 "${X[@]}"
.
a b
您是否看到规范文本中有关字段拆分的注释? "where field splitting (see Field Splitting) is performed"?这就是这里的谜团的关键。
除了引号外,扩展的行为是相同的。区别在于之后会发生什么。具体来说,field/word分裂。
显示问题的最简单方法是 运行 您的代码启用 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
被调用时,除了最后一个案例 |
已经消失 .
它已经消失的原因是因为没有引号,变量扩展的 结果 (在本例中是 *
扩展)被 field/word 拆分.并且由于 IFS
用于 both 来组合正在扩展的字段然后再次拆分它们 |
被字段拆分吞没了。
为了完成解释(对于实际有问题的情况),即使在调用中引用了扩展的版本(即扩展为 log1 "${X[*]}"
,log1
失败的原因log1 "a|b"
正确)是因为 log1
本身 没有使用 @
的引用扩展,所以函数中 @
的扩展是本身分词(正如 echo a b
在 log1
案例以及所有其他 log1
案例中所见)。
IFS
不仅用于连接 ${X[*]}
的元素,还用于拆分未引用的扩展 $@
。对于 log1 "${X[*]}"
,会发生以下情况:
"${X[*]}"
按预期扩展为a|b
,因此</code> 在 <code>log1
. 中设置为 - 展开
$@
(未加引号)时,结果字符串为a|b
. - 未加引号的扩展以
|
为分隔符进行分词(由于IFS
的全局值),因此echo
接收两个参数,a
和b
.
a|b