set -- $args
行在這裡做什麼,為什麼它在 Zsh 和 Bash 之間的行為不同?
在 Mac OSX 附帶的版本的手冊頁中,
getopt
給出了一個使用args=$(getopt optstring $*); set -- $args
. 在這裡做什麼set -- $args
?此外,考慮函式和測試字元串
f () { args=$(getopt o: $*) set -- $args for i; do echo $i done } f -o 123 xyz
在 Bash 3.2 和 4.3 中,這會產生
-o 123 -- xyz
但在 Zsh 5.1 中,它產生
-o 123 -- xyz
是什麼導致了差異?是因為 Zsh 不同的分詞行為,還是其他原因?我嘗試了引用和參數擴展標誌的不同組合,但無法真正弄清楚這兩個 shell 中到底發生了什麼。
set -- $args
根據 的內容設置位置參數$args
。zsh
現在您將獲得與其他 POSIX shell之間的不同行為。因為預設
zsh
不進行欄位拆分,所以會得到一個字元串,即$args
.bash
您必須顯式呼叫拆分以獲得與(以及其他 POSIX shell)相同的行為:set -- ${=args}
bash
執行Field Splitting
的內容$args
,生成四個字元串。您可以檢查$#
以了解 . 之後的位置參數的數量set -- $args
。請注意,如果是
bash
,您也應該添加set -f
關閉萬用字元。
這是糟糕的程式碼。
getopt
幾乎無法使用¹。改用getopts
shell 內置(它有兩個優點:它可以工作,並且在所有 POSIX 平台上都可用)。什麼
args=$(getopt optstring $*); set -- $args
是:
- 在空格處拆分每個命令行參數,並將其替換為空格分隔的單詞列表。
- 獲取每個結果單詞並將其解釋為萬用字元模式。如果該模式與至少一個文件匹配,則將該模式替換為匹配文件列表。
- 將生成的單詞列表傳遞給
getopt
命令。- 將
getopt
命令的輸出儲存在變數中args
。這是一個字元串,由以空格作為分隔符的命令行參數連接在一起,並重新排序以具有選項及其參數,然後--
是 ,然後是非選項操作數。- 在空格處拆分此字元串,將每個單詞解釋為萬用字元模式,並用匹配文件列表(如果有)替換該模式。
- 使用結果列表作為位置參數。
在 Bourne/POSIX 樣式的 shell 中,
$foo
是“split+glob”運算符。您需要雙引號來獲取變數的值:"$foo"
。但是在 zsh 中,$foo
工作方式不同:它擴展為foo
except whenfoo
為空的值。所以在 zsh 你得到一個位置參數。可以通過編寫來避免第一次使用 split+glob 運算符
getopt optstring "$@"
:這會將位置參數準確地傳遞給getopt
. 但是在解析 的輸出時getopt
,拆分是不可避免的(儘管您可以禁用萬用字元),因為getopt
使用空格來分隔參數。問題getopt
在於輸出不明確:無法區分參數中的空格和getopt
添加為分隔符的空格。正確的方法是使用
getopts
. 它堅固且便攜。while getopts optstring OPTLET; do # We got the option -$OPTLET case $OPTLET in … esac done shift $((OPTIND-1)) # Now the positional parameters are just the non-option operands
¹至少是 BSD 版本。GNU 版本添加了使其可用的功能。