web-dev-qa-db-ja.com

a = "$ @"の予期しない結果

私はこの状況に苦しんでいます:

$ set -- 1 2 3
$ a="$@"
$ echo "$a"
1 2 3

私が予想外だと思うのは、割り当て自体です。

man bashは、"$@"拡張について次のように述べています。

展開が二重引用符で囲まれている場合、各パラメーターは個別のWordに展開されます。

したがって、これは次のようになります。

b="1" "2" "3"
bash: 2: command not found

そして、それが"$*"拡張の目的です。

展開が二重引用符で囲まれている場合は、各パラメーターの値がIFS特殊変数の最初の文字で区切られた単一のWordに展開されます。つまり、「$ *」は「$ 1c $ 2c ...」と同等です。ここで、cはIFS変数の値の最初の文字です。 IFSが設定されていない場合、パラメーターはスペースで区切られます。 IFSがnullの場合、パラメーターは区切り文字を介さずに結合されます。

したがって、これは正しいはずです。

$ set -- 1 2 3
$ a="$*"
$ echo "$a"
1 2 3

では、なぜ"$@"が同じ結果になるのでしょうか?この点で異なるはずです。これはBashの問題ですか、それとも私の誤解ですか?

Shellcheck これを SC2124 として検出します。 SC2145 をトリガーする例も提供できます。

それはで観察されます:

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
4.9.0-6-AMD64 #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02) x86_64 GNU/Linux
7
user147505

私の知る限り、 POSIXは割り当ての$@を未定義のままにします なので、Bashのドキュメントを除いて、実際にはバグではありません。 $@は次の2つの場合に定義されます。

  • フィールド分割が実行されるコンテキストで拡張が発生した場合...
  • 展開が二重引用符で囲まれている場合、[...]フィールド分割が実行されない限り、動作は指定されません[...] (*)

だが、

他のすべてのコンテキストでは、展開の結果はunspecifiedです。

フィールド分割は割り当てでは発生せず、二重引用符がなくても発生しないため、未定義です。


ここで、a="$@"a="$*"とほぼ同じように機能すると思います。これは、複数の単語に展開しても、ここではまったく意味がないためです。複数の単語を個別のエンティティとして通常の変数に割り当てることはできません。1つを割り当てると、残りをコマンド引数として使用すると、混乱し、バグが発生しやすくなります。

そのシェルチェックページにあるように、割り当てにおける"$@"の動作はシェル間で異なります。 Bashとkshは、位置パラメーターをスペースで結合し、zshとダッシュはIFSの最初の文字で結合します("$*"とまったく同じです)。

$ bash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ ksh93 -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ zsh -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>
$ dash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>

単一の文字列に結合する場合はa="$*"を使用するか、必要なものを明示的に記述するのがおそらく最善です。

(*または${parameter:-Word}拡張を含む別のケース)

8
ilkkachu