考虑以下示例:
IFS=:
x="a :b" # three spaces
echo ["$x"] # no word splitting
# [a :b] # as is
echo [$x] # word splitting
# [a b] # four spaces
分词识别单词"a "
(三个空格)和"b"
,用冒号分隔,然后echo
用中间的空格连接单词。
但是,当使用 的值$x
作为函数参数时,我发现很难解释结果。
args(){ echo ["$*"];}
args a :b # three spaces
# [a::b]
和:
args(){ echo [$*];}
args a :b # three spaces
# [a b] # two spaces
$*
扩展为所有位置参数组合的值。此外,"$*"
等价于"$1c$2"
,其中c
是 IFS 变量值的第一个字符。
args(){ echo ["$1"]["$2"]; }
args a :b # three spaces
# [a][:b]
和:
args(){ echo [$1][$2]; }
args a :b # three spaces
# [a][ b]
当有未引用的扩展时,应该总是发生分词。这里"$1"
和$1
是相同的,并且在这两种情况下它们都不使用:
分隔符。[$2]
->[ b]
也不清楚。
可能在应用 IFS 拆分之前,使用了其他标记化规则,但我找不到它们。
分词仅适用于现代 Bourne-like shell 中的不带引号的扩展(参数扩展、算术扩展和命令替换)(在 中
zsh
,除非您使用仿真模式,否则仅命令替换)。当你这样做时:
完全不涉及分词。
将这些标记化的是 shell 解析,发现第一个不是它的关键字之一,因此它是一个带有 3 个参数的简单命令
args
:a
和:b
. 那里的空间量不会有任何区别。请注意,它不仅是空格,还有制表符,并且在某些 shell(如yash
或bash
)中,任何在您的语言环境中被视为空白的字符(尽管在 的情况下bash
,而不是多字节字符)¹。即使在 Bourne shell 中,无论它们是否是扩展的结果,分词也适用于命令的未引用参数,这将在标记化和语法解析的顶部(很久之后)完成。
在 Bourne shell 中,在
那不会将其解析为:
但首先作为
while
带有一个简单命令的a ,并且该简单命令的edit
单词(因为它是一个参数,而不是bid=did
作为赋值的单词)将被进一步拆分为ed
and ,t
以便ed
带有 3 个参数的命令ed
,t
并且foo
将作为该while
循环的条件。分词不是语法解析的一部分。它就像一个隐式应用于参数的运算符(也在
for
循环词、数组和一些 shell 中重定向的目标和一些其他上下文),用于它们中未引用的部分。令人困惑的是它是隐式完成的。你不做cmd split($x)
,你做cmd $x
,并且split()
(实际上glob(split())
)是暗示的。在zsh
中,您必须明确请求它进行参数扩展(split($x)
是否$=x
存在($=
看起来像一把剪刀))。所以,现在,对于你的例子:
a
和:b
参数args
join 与第一个字符$IFS
给出a::b
(请注意,[...]
在这里使用它是一个坏主意,因为它是一个通配符运算符)。$*
(其中包含a::b
)被拆分为a
、空字符串和b
。所以是:毫不奇怪,因为没有分词。
这就像:
as
$2
(:b
) 将被拆分为空字符串和b
.您将看到实现之间变化的一种情况是何时
$IFS
为空。在:
在某些 shell(现在大多数)中,您会看到
<ab>
即使不会"$*"
扩展到ab
. 这些外壳仍然将这些参数a
和b
位置参数分开,并且现在已在最新版本的标准中成为 POSIX 要求。如果你这样做了:
您会看到
<ab>
分配给.a
b
$var
¹,当然,分隔单词的不仅仅是空格。shell 语法中的特殊标记也是如此,其列表取决于上下文。在大多数情况下,
|
,||
,&
,;
, 换行符,<
,>
,>>
... 分隔单词。例如ksh93
,您可以编写一个无空白命令,例如: