12

I've been surprised with the line marked (!!) in the following example:

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

Here is my understanding of the behavior:

  • ${X[*]} and ${X[@]} both expand to a b
  • "${X[*]}" expands to "a|b"
  • "${X[@]}" expands to "a" "b"
  • $* and $@ have the same behavior as ${X[*]} and ${X[@]}, except for their content being the parameters of the program or function

This seems to be confirmed by the bash manual.

In the line log1 "${X[*]}", I therefore expect the quoted expression to expand to "a|b", then to be passed to the log1 function. The function has a single string parameter which it displays. Why does something else happen?

It'd be cool if your answers were backed by manual/standard references!

4

3 回答 3

7

IFS不仅用于连接 的元素${X[*]},还用于拆分未引用的扩展$@。对于log1 "${X[*]}",会发生以下情况:

  1. "${X[*]}"扩展a|b为预期,因此$1设置为a|binside log1
  2. $@(unquoted) 展开时,结果字符串是a|b.
  3. 不带引号的扩展|以分隔符进行分词(由于 的全局值IFS),因此echo接收两个参数ab
于 2015-07-28T13:14:50.293 回答
5

那是因为$IFS设置为|

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

输出:

a b

man bash说:

IFS 用于扩展后分词的内部字段分隔符...

于 2015-07-28T13:08:18.260 回答
3

在[特殊参数[(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02)的POSIX规范部分中,我们发现。

@

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

*

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

所以从引用的变体开始(它们更简单):

我们看到*扩展“将 [s] 扩展为单个字段,每个参数的值由 IFS 变量的第一个字符分隔”。这就是你a|becho "${X[*]"and得到的原因log2 "${X[*]}"

我们还看到@扩展扩展为“每个位置参数都应扩展为单独的字段”。这就是你a becho "${X[@]}"and得到的原因log2 "${X[@]}"

您是否在规范文本中看到有关字段拆分的说明?“在哪里执行字段拆分(请参阅字段拆分)”?这就是这里的奥秘所在。

在引号之外,扩展的行为是相同的。不同之处在于之后会发生什么。具体来说,字段/单词拆分。

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"),它也失败的原因是因为log1 它本身不使用引用的扩展,@所以扩展函数中的of本身就是分词的(在这种情况下以及所有其他情况下@都可以看出)。echo a blog1log1

于 2015-07-28T13:14:26.130 回答