Variable

位置參數中的分詞

  • December 4, 2017

考慮以下範例:

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 個參數的簡單命令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帶有一個簡單命令的a ,並且該簡單命令的edit單詞(因為它是一個參數,而不是bid=did作為賦值的單詞)將被進一步拆分為edand ,這樣t帶有ed3 個參數的命令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參數argsjoin 與第一個字元$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. 這些外殼仍然將這些參數ab位置參數分開,並且現在已在最新版本的標準中成為 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

引用自:https://unix.stackexchange.com/questions/408620