Bash

set -- $args 行在這裡做什麼,為什麼它在 Zsh 和 Bash 之間的行為不同?

  • September 23, 2018

在 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根據 的內容設置位置參數$argszsh現在您將獲得與其他 POSIX shell之間的不同行為。

因為預設zsh不進行欄位拆分,所以會得到一個字元串,即$args. bash您必須顯式呼叫拆分以獲得與(以及其他 POSIX shell)相同的行為:

set -- ${=args}

bash執行Field Splitting的內容$args,生成四個字元串。您可以檢查$#以了解 . 之後的位置參數的數量set -- $args

請注意,如果是bash,您也應該添加set -f關閉萬用字元。

這是糟糕的程式碼。getopt幾乎無法使用¹。改用getoptsshell 內置(它有兩個優點:它可以工作,並且在所有 POSIX 平台上都可用)。

什麼args=$(getopt optstring $*); set -- $args是:

  • 在空格處拆分每個命令行參數,並將其替換為空格分隔的單詞列表。
  • 獲取每個結果單詞並將其解釋為萬用字元模式。如果該模式與至少一個文件匹配,則將該模式替換為匹配文件列表。
  • 將生成的單詞列表傳遞給getopt命令。
  • getopt命令的輸出儲存在變數中args。這是一個字元串,由以空格作為分隔符的命令行參數連接在一起,並重新排序以具有選項及其參數,然後--是 ,然後是非選項操作數。
  • 在空格處拆分此字元串,將每個單詞解釋為萬用字元模式,並用匹配文件列表(如果有)替換該模式。
  • 使用結果列表作為位置參數。

在 Bourne/POSIX 樣式的 shell 中,$foo是“split+glob”運算符。您需要雙引號來獲取變數的值:"$foo"。但是在 zsh 中,$foo工作方式不同:它擴展為fooexcept 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 版本添加了使其可用的功能。

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