位置参数中的分词

位置参数中的分词

考虑以下示例:

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 个参数的简单命令:argsa:b。空间的大小不会有任何区别。请注意,它不仅是空格,还包括制表符,在某些 shell 中(例如yashbash),任何被视为空白的在您的语言环境中(尽管在 的情况下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,tfoo将作为该循环的条件运行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 仍然将这些参数ab位置参数分开,并且现在已在最新版本的标准中提出了 POSIX 要求。

如果你这样做:

set a b
IFS=
var="$*" # note that the behaviour for var=$* is unspecified
printf '<%s>\n' $var

您会看到,当分配给 时,和是两个单独的参数<ab>的信息丢失了。ab$var


1 当然,分隔单词的不仅仅是空格。 shell 语法中的特殊标记也可以,其列表取决于上下文。在大多数情况下,|||&;、 换行符、<>>>... 分隔单词。例如ksh93,您可以编写一个无空白命令,例如:

while({([[(:)]])})&&((1||1))do(:);uname<&2|tee>(rev)file;done

相关内容