考虑以下示例:
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 分割之前,使用了其他标记化规则,但我无法找到它们。
答案1
分词仅适用于现代类似 Bourne 的 shell 中的不带引号的扩展(参数扩展、算术扩展和命令替换)(在 中zsh
,仅命令替换,除非您使用模拟模式)。
当你这样做时:
args a :b
根本不涉及分词。
shell 解析将这些标记化,发现第一个不是它的关键字之一,因此它是一个带有 3 个参数的简单命令:args
、a
和:b
。空间的大小不会有任何区别。请注意,它不仅是空格,还包括制表符,在某些 shell 中(例如yash
或bash
),任何被视为空白的在您的语言环境中(尽管在 的情况下bash
,不是多字节的) 。
即使在 Bourne shell 中,分词也适用于命令的未加引号的参数,无论它们是否是扩展的结果,这都会完成在上面(很久之后)标记化和语法解析。
在 Bourne shell 中,
IFS=i
while bib=did edit foo
那不会解析即:
"wh" "le b" "b=d" "d ed" "t foo"
但首先作为while
一个简单的命令,该简单命令的edit
单词(因为它是一个参数,而不是bid=did
一个赋值的单词)将是更远分成ed
和 ,t
以便ed
带有 3 个参数的命令ed
,t
和foo
将作为该循环的条件运行while
。
分词是不是语法分析的一部分。它就像一个隐式应用于参数的运算符(也在for
循环单词、数组中以及某些 shell 中的重定向目标和其他一些情况) 对于其中未引用的部分。令人困惑的是它已经完成了隐含地。你不做cmd split($x)
,你做cmd $x
,然后split()
(实际上glob(split())
) 是隐含的。在 中zsh
,您必须明确请求它以进行参数扩展(split($x)
是否$=x
存在($=
看起来像一把剪刀))。
现在,举个例子:
args(){ echo ["$*"];} args a :b # three spaces # [a::b]
a
和join:b
的参数args
,其中第一个字符$IFS
给出a::b
(请注意,在这里使用它是一个坏主意,[...]
因为它是一个通配符运算符)。
args(){ echo [$*];} args a :b # three spaces # [a b] # two spaces
$*
(其中包含a::b
) 被拆分为a
、空字符串 和b
。所以就是:
echo '[a' '' 'b]'
args(){ echo ["$1"]["$2"]; } args a :b # three spaces # [a][:b]
毫不奇怪,因为没有分词。
args(){ echo [$1][$2]; } args a :b # three spaces # [a][ b]
这就像:
echo '[a]' '[' 'b]'
as $2
( :b
) 将被拆分为空字符串 和b
.
您会看到实现之间存在差异的一种情况是当$IFS
为空时。
在:
set a b
IFS=
printf '<%s>\n' $*
在一些 shell 中(现在大多数),你会看到
<a>
<b>
即使<ab>
会"$*"
扩展到ab
.这些 shell 仍然将这些参数a
和b
位置参数分开,并且现在已在最新版本的标准中提出了 POSIX 要求。
如果你这样做:
set a b
IFS=
var="$*" # note that the behaviour for var=$* is unspecified
printf '<%s>\n' $var
您会看到,当分配给 时,和是两个单独的参数<ab>
的信息丢失了。a
b
$var
1 当然,分隔单词的不仅仅是空格。 shell 语法中的特殊标记也可以,其列表取决于上下文。在大多数情况下,|
、||
、&
、;
、 换行符、<
、>
、>>
... 分隔单词。例如ksh93
,您可以编写一个无空白命令,例如:
while({([[(:)]])})&&((1||1))do(:);uname<&2|tee>(rev)file;done