ここで `set --$args` 行は何をして、 Zsh と Bash の間で異なる動作をするのはなぜですか?

ここで `set --$args` 行は何をして、 Zsh と Bash の間で異なる動作をするのはなぜですか?

getopt設定の使用例は、Mac OSXに付属のバージョンのマンページにあります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の別の単語分割動作のせいですか、それとも別の理由ですか?引用フラグとパラメータ拡張フラグのさまざまな組み合わせを試しましたが、実際に2つのシェルで何が起こっているのかを理解することはできません。

答え1

これは悪いコードです。getoptほとんど利用できません。getopts組み込みシェルを使用してください(2つの利点があります。それはうまくいくこととすべてのPOSIXプラットフォームで動作することです)。

これがすることはargs=$(getopt optstring $*); set -- $args

  • 各コマンドライン引数をスペースに分割し、スペースで区切られた単語のリストに置き換えます。
  • 各結果の単語を取得し、ワイルドカードパターンとして解釈します。パターンが 1 つ以上のファイルと一致する場合は、パターンを一致するファイルのリストに置き換えます。
  • 結果の単語リストをgetoptコマンドに渡します。
  • コマンドの出力をgetopt変数に保存しますargs。これは、スペースで区切られたコマンドライン引数、オプションとその引数、オプションではなく--オペランドで順序付けられた文字列です。
  • この文字列をスペースに分割し、各単語をワイルドカードパターンとして解釈し、パターンを一致するファイルのリスト(存在する場合)に置き換えます。
  • 結果のリストを位置引数として使用します。

Bourne/POSIX スタイルのシェルでは、$foo「split+glob」演算子です。変数の値を取得するには二重引用符が必要です。"$foo"。しかし、zshでは異なる動作をします。$foonull の場合を除き、値に拡張されます。したがって、zshは位置パラメータを取得します。foofoo

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バージョンには、それを使用するための機能が追加されました。

答え2

set -- $argsの内容に基づいて位置パラメータを設定します$args。これでzsh、他のPOSIXシェルとは異なる動作があります。

zsh実装されていないフィールド分割デフォルトでは、内容である文字列を取得します$argsbash(および他のPOSIXシェル)と同じ動作を得るには、明示的に分割を呼び出す必要があります。

set -- ${=args}

bashField Splittingcontentで実行すると、$args4つの文字列が生成されます。$#その後、位置パラメータの数を確認できますset -- $args

その場合は、ワイルドカードをオフにする必要がありますbashset -f

関連情報