Unerwartetes Ergebnis von a = "$ @"

7

Ich kämpfe mit dieser Situation:

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

Was ich unerwartet finde, ist die Aufgabe selbst.

man bashsagt dies über die "$@"Erweiterung:

Wenn die Erweiterung in doppelten Anführungszeichen erfolgt, wird jeder Parameter zu einem separaten Wort erweitert.

Das sollte also analog sein zu:

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

Und dafür ist die "$*"Erweiterung gedacht:

Wenn die Erweiterung in doppelten Anführungszeichen erfolgt, wird sie zu einem einzelnen Wort erweitert, wobei der Wert jedes Parameters durch das erste Zeichen der IFS-Spezialvariablen getrennt ist. Das heißt, "$ *" entspricht "$ 1c $ 2c ...", wobei c das erste Zeichen des Werts der IFS-Variablen ist. Wenn IFS nicht gesetzt ist, werden die Parameter durch Leerzeichen getrennt. Wenn IFS null ist, werden die Parameter ohne dazwischenliegende Trennzeichen verbunden.

Und so sollte das richtig sein:

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

Warum ergibt sich "$@"das gleiche? Sie sollen sich in diesem Punkt unterscheiden. Ist das ein Bash-Problem oder mein Missverständnis?

Shellcheck erkennt dies als SC2124 . Ich kann auch ein Beispiel liefern, das SC2145 auslöst .

Es wird beobachtet am:

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

quelle

Antworten:

8

Soweit ich das beurteilen kann, lässt POSIX $@eine Aufgabe undefiniert , so dass es kein wirklicher Fehler ist, außer vielleicht in Bashs Dokumentation. $@wird in zwei Fällen definiert:

  • Wenn die Erweiterung in einem Kontext erfolgt, in dem die Feldaufteilung durchgeführt wird ...
  • Wenn die Erweiterung in doppelten Anführungszeichen erfolgt, ist das Verhalten nicht angegeben, es sei denn, [...] eine Feldaufteilung würde durchgeführt [...] (*)

Aber,

In allen anderen Zusammenhängen sind die Ergebnisse der Erweiterung nicht spezifiziert .

Feldaufteilung findet in einer Zuweisung nicht statt und würde auch ohne doppelte Anführungszeichen nicht stattfinden. Das ist also undefiniert.


Nun gehe ich davon aus, dass sich a="$@"das ungefähr so ​​verhält, als würde eine a="$*"Erweiterung auf mehrere Wörter hier keinen Sinn ergeben. Sie können einer regulären Variablen nicht mehrere Wörter als unterschiedliche Entitäten zuweisen, und die Zuweisung eines Wortes, aber die Verwendung des Restes als Befehlsargumente wäre verwirrend und fehleranfällig.

Wie auf dieser Shellcheck-Seite angegeben, unterscheidet sich das Verhalten "$@"in einer Zuweisung zwischen den Shells. Bash und ksh verbinden die Positionsparameter mit Leerzeichen, zsh und dash mit dem ersten Buchstaben von IFS(genau wie "$*"dies der Fall wäre).

$ 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>

Es ist wahrscheinlich am besten zu verwenden, a="$*"wenn Sie einer einzelnen Zeichenfolge beitreten oder auf andere Weise explizit schreiben möchten, was Sie möchten.

(* oder ein anderer Fall mit ${parameter:-word}Erweiterungen)

ilkkachu
quelle