位置參數中的分詞
考慮以下範例:
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
,除非您使用仿真模式,否則僅命令替換)。當你這樣做時:
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
帶有一個簡單命令的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
存在($=
看起來像一把剪刀))。所以,現在,對於你的例子:
args(){ echo ["$*"];} args a :b # three spaces # [a::b]
a
和:b
參數args
join 與第一個字元$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
. 這些外殼仍然將這些參數a
和b
位置參數分開,並且現在已在最新版本的標準中成為 POSIX 要求。如果你這樣做了:
set a b IFS= var="$*" # note that the behaviour for var=$* is unspecified printf '<%s>\n' $var
您會看到
<ab>
分配給.a``b``$var
¹,當然,分隔單詞的不僅僅是空格。shell 語法中的特殊標記也是如此,其列表取決於上下文。在大多數情況下,
|
,||
,&
,;
, 換行符,<
,>
,>>
… 分隔單詞。例如ksh93
,您可以編寫一個無空白命令,例如:while({([[(:)]])})&&((1||1))do(:);uname<&2|tee>(rev)file;done