Inspiriert von dieser aktuellen Frage :
bash$ a=(1 2 3)
bash$ echo $a
1
aber
zsh% a=(1 2 3)
zsh% echo $a
1 2 3
zsh% printf '%s\n' $a
1
2
3
(Der letzte Teil zeigt, dass das Array in separate Argumente erweitert wird, die äquivalent zu "${a[@]}"
und nicht sind. "${a[*]}"
)
Das Bash-Verhalten (das mit ksh übereinstimmt) ist äußerst kontraintuitiv. Wie ist "nur erstes Element" eine vernünftige Reaktion auf "diese Array-Variable erweitern"?
In anderen Bereichen, in denen zsh divergiert, liegen ksh und bash näher an der ursprünglichen Bourne-Shell. Bourne hatte jedoch keine benutzerdefinierten Array-Variablen.
Warum hat Bash diese seltsame Entscheidung getroffen? Wenn es ksh kopierte, warum traf ksh diese seltsame Entscheidung?
Fortsetzung nach einer langen Reihe von Kommentaren:
Dies sollte keine Frage der Kritik oder des Lobes von zsh sein. zsh ist nichts anderes als ein leicht zugängliches Beispiel dafür, wie Dinge anders gemacht worden sein könnten .
Eine der Möglichkeiten, eine Entwurfsentscheidung zu erklären, ist die Abwärtskompatibilität. Und Abwärtskompatibilität ist keine Meinung. Es ist eine objektive Tatsache.
Wenn Sie ein Skript anzeigen können (ein vollständiges Skript , kein Auszug außerhalb des Kontexts), das sich in der Bourne-Shell auf bekannte Weise verhält (dh nicht nur mit einem Syntaxfehler bombardiert) und sich in der hypothetischen "Korn-Shell" anders verhält mit voller $ array Erweiterung ", dann gewinnen Sie! Es ist ein Problem mit der Abwärtskompatibilität.
Ein solches Skript wurde nicht angegeben. Dies ist nicht einer:
a=(1 2 3)
printf '%s\n' $a
weil es ein Syntaxfehler in der Bourne-Shell ist. Wenn Sie einem früheren Syntaxfehler eine neue Bedeutung geben, können Sie neue Funktionen erstellen und gleichzeitig die Abwärtskompatibilität beibehalten.
Soweit ich das beurteilen kann, führt die Tatsache, dass a=(...)
es sich ursprünglich um einen Syntaxfehler handelte, zu einer sauberen Trennung zwischen Skripten, die Arrays verwenden (versuchen) und solchen, die dies nicht tun. In der ersten Kategorie kann die Abwärtskompatibilität nicht als Grund für irgendetwas herangezogen werden, da diese Skripte ohnehin nicht in der alten Shell ausgeführt würden. In der zweiten Kategorie gilt die Abwärtskompatibilität unabhängig von den Erweiterungsregeln für Arrayvariablen, da keine zu erweiternden Arrays vorhanden sind!
Dies ist kein Beweis, da ich mich teilweise auf die Intuition verlasse, um zu entscheiden, dass es keine Möglichkeit gibt, ein Array ohne ein Skript zu schmuggeln, =(
und daher kein Skript existiert, das inkompatibles Verhalten aufweisen würde. Das Schöne an einer Behauptung der Nichtexistenz ist, dass Sie nur ein Gegenbeispiel zeigen müssen, um sie zu beenden.
Die a=$@
Sache, die in den Kommentaren angesprochen wurde, scheint zu einer Erklärung beizutragen. Wenn es eine Array-Variable erstellt, a
dann dieses Skript:
a=$@
printf '%s\n' $a
sollte den Unterschied zeigen. In meinen Tests passiert das jedoch nicht. Alle Muscheln (Erbstück sh, modernes ksh, bash und zsh) scheinen die erste Zeile auf die gleiche Weise zu behandeln. a
ist kein Array, sondern nur eine Zeichenfolge mit Leerzeichen. (zsh divergiert in der zweiten Zeile, weil es keine Wortteilung für den Wert von macht $a
, aber das hat nichts mit Array-Variablen zu tun)
a
ein Array in zsh enthalten könnte, warum ina=$@
,$a
ist eine Zeichenfolge?=
Zeichen eine linke Klammer steht .a=($@)
kopiert das Array als Array.a=(1 2 3); printf '%s\n' $a
konnte nicht mit 'b = $ a' kopiert werden, ist das nicht seltsam? In yash wird das Array kopiertb=$a
.${array[0]}
möglicherweise nicht vorhanden, insbesondere bei assoziativen Arrays.Antworten:
Ich kann keine Antwort geben, aber einige mögliche Erklärungen vorschlagen.
Es stimmt , dass mit Ausnahme von KSH und ihre Klone (pdksh und weitere Derivate und bash), alle anderen Schalen mit Arrays (
csh
,tcsh
,rc
,es
,akanga
,fish
,zsh
,yash
) haben ,$array
um alle Mitglieder des Arrays zu erweitern.Aber in beiden
yash
undzsh
(wenn in dersh
Emulation), den beiden Bourne-ähnlichen Shells in dieser Liste, unterliegt diese Erweiterung immer noch dem Split + Glob (und der leeren Entfernung,zsh
auch wenn sie nicht in dersh
Emulation ist), sodass Sie immer noch die verwenden müssen umständliche"${array[@]}"
Syntax (oder"${(@)array}"
oder"$array[@]"
inzsh
der es kaum einfacher zu tippen ist), um die Liste beizubehalten (csh
undtcsh
ähnliche Probleme zu haben). Diese Split + Glob- und Leerentfernung ist das Bourne-Erbe (das in gewissem Maße durch das Thompson-Shell-Erbe verursacht wurde, das$1
eher einer Makroerweiterung ähnelte).rc
undfish
sind zwei Beispiele für spätere Granaten, die kein Bourne-Gepäck haben und sauberer sind. Sie erkennen die Tatsache an, dass eine Shell ein Befehlszeileninterpreter ist. Die primären Dinge, mit denen sie sich befassen, sind Listen (die Liste der Argumente für Befehle), also ist list / array der primäre Datentyp (es gibt nur einen Typ und seine Listen inrc
) und Der Split + Glob-upon-Expansion-Fehler / die Fehlfunktion der Bourne-Shell (die jetzt, da der primäre Typ Array ist, nicht mehr benötigt wird) wurde beseitigt.Dies erklärt jedoch nicht, warum David Korn sich dafür entschieden hat, nicht
$array
auf alle Elemente, sondern auf das Element des Index 0 zu erweitern.Abgesehen von
csh
/tcsh
sind all diese Shells jetzt viel neuer als dieksh
, die in den frühen 80ern nur wenige Jahre nach der Veröffentlichung der Bourne-Shell und von Unix V7 entwickelt wurden. Unix V7 war dasjenige, das auch die Umgebung einführte. Das war damals die schicke neue Sache. Die Umgebung ist ordentlich und nützlich, aber Umgebungsvariablen können keine Arrays enthalten, es sei denn, Sie verwenden irgendeine Form der Codierung.Das ist nur eine Vermutung, aber ich vermute, ein Grund für David Korn, diesen Ansatz zu wählen, war, dass die Schnittstelle zur Umgebung nicht geändert wurde.
In ksh88 waren wie rc alle Variablen Arrays (allerdings spärlich; ein bisschen wie assoziative Arrays mit Schlüsseln, die auf positive ganze Zahlen beschränkt sind, was eine weitere Kuriosität im Vergleich zu anderen Shells oder Programmiersprachen darstellt, und man konnte feststellen, dass sie nicht vollständig durchdacht waren es war zum Beispiel unmöglich, die Liste der Schlüssel abzurufen). In diesem neuen Design
var=value
wurde kurz fürvar[0]=value
. Sie können weiterhin alle Ihre Variablenexport var
exportieren , exportieren jedoch das Element des Index 0 des Arrays in die Umgebung.rc
setzt alle seine Variablen in die Umgebung,fish
unterstützt das Exportieren von Arrays, aber um dies für Arrays mit mehr als einem Element zu tun (zumindest für den Port zu Unix für rc, der von plan9 stammt), müssen sie auf irgendeine Form der Codierung zurückgreifen was nur von ihnen verstanden wird.csh
,tcsh
,zsh
Nicht unterstützt Arrays exportieren (obwohl heute , dass nicht wie eine große Einschränkung klingen mag). Sie können Arrays inyash
exportieren, aber sie werden als Umgebungsvariable exportiert, mit der die Array-Elemente verknüpft sind:
((a "" "" b)
und(a : b)
daher auf denselben Wert exportiert werden), und beim Importieren wird keine Rückkonvertierung in ein Array durchgeführt.Eine andere mögliche Rechtfertigung könnte die Konsistenz mit Bournes
$@
/ gewesen sein$*
(aber warum beginnen Array-Indizes dann bei 0 statt bei 1 (eine weitere Kuriosität im Vergleich zu anderen Shells / Sprachen der Zeit)?).ksh
war keine freie Software, es war ein kommerzielles Unternehmen, eine der Anforderungen war die Bourne-Kompatibilität.ksh
entfernte die Feldaufteilung, die für jedes nicht zitierte Wort im Listenkontext vorgenommen wurde (da dies in der Bourne-Shell eindeutig nicht nützlich war), musste sie jedoch für Erweiterungen behalten (da Skripte Dinge verwendeten, wievar="file1 file2"; cmd $var
die Bourne-Shell kein Array hatte, aber"$@"
) . Das in einer Shell zu belassen, die ansonsten Arrays enthält, macht wenig Sinn, aber Korn hatte kaum eine andere Option, wenn Ksh noch in der Lage sein sollte, Skripte der Verbraucherbasis zu interpretieren. Wenn$scalar
unterlag split + glob,$array
müsste aus Gründen der Konsistenz sein, und so machte"${array[@]}"
eine Verallgemeinerung"$@"
Sinn.zsh
hatte keine ähnliche Einschränkung, so dass es frei war, den split + glob bei Erweiterungen gleichzeitig mit dem Hinzufügen von Arrays zu entfernen (aber einen Preis für das Brechen von Bourne abwärtskompatibel zu zahlen).Eine andere Erklärung , wie @Arrow angeboten hätte sein können , dass er nicht die bestehenden Betreiber zu überlasten wollen sie anders für verschiedene Arten von Variablen machen verhalten (zum Beispiel
${#var}
vs${#array}
obwohl die Bourne - Shell nicht haben , dass ein oder${var-value}
,${var#pattern}
) dem Verwirrung bei den Benutzern verursachen (zsh
es ist nicht immer offensichtlich, wie einige Operatoren mit Array oder Skalar arbeiten).Einige verwandte Lektüre:
ksh
(Arrays, die zuerst als Patches über die Bourne-Shell für ein Formulareintragssystem hinzugefügt wurden ) erklärt. .Was den
a=$@
Fall in Ihrer Bearbeitung betrifft, ist dies tatsächlich ein Fall, in dem ksh die Kompatibilität mit der Bourne-Shell unterbrochen hat.In der Bourne-Shell
$@
und$*
enthielt die Verkettung der Positionsparameter mit Leerzeichen. Nur$@
wenn zitiert, war speziell, da es auf das gleiche erweitert wurde,"$*"
aber mit den eingefügten Leerzeichen nicht zitiert (mit Sonderfällen für die leere Liste in den neueren Versionen, in denen es wie unter Solaris behandelt wurde). Sie werden feststellen, dass beim Entfernen von Leerzeichen$IFS
in"$@"
Listenkontexten nur ein Argument angezeigt wird (0 für eine leere Liste in den oben genannten festen Versionen). Wenn nicht angegeben,$*
und$@
verhalten sich wie jede andere Variable (split auf Zeichen von$IFS
, nicht notwendigerweise auf den ursprünglichen Positionsparameter). Zum Beispiel in der Bourne-Shell:Würde ausgeben:
Ksh88 änderte das so
$@
und$*
wurde mit dem ersten Charakter von verbunden$IFS
."$@"
Im Listenkontext werden die Positionsparameter getrennt, außer wenn sie$IFS
leer sind.Wenn
$IFS
leer ist,$*
werden sie im Leerzeichen verbunden, außer$*
wenn sie in Anführungszeichen stehen und ohne Trennzeichen verbunden werden.Beispiele:
Sie werden viele Variationen in den verschiedenen Bourne / Korn-ähnlichen Muscheln sehen, einschließlich ksh93 vs ksh88. Es gibt auch einige Variationen in Fällen wie:
Oder wenn es Mehrbytezeichen
$IFS
enthält oder Bytes keine gültigen Zeichen bilden.quelle
Von Ihren Kommentaren zu dieser Antwort erwarten Sie, dass eine Antwort sagt und akzeptiert, dass das Paradigma von zsh "besser" ist und daher die Art und Weise, wie alle Shells funktionieren sollten. "Besser" ist nur eine Meinung. Eine Stellungnahme bedarf keiner Diskussion.
Vielleicht erwarten Sie das Detail, warum
$a
es auch für ein Array verwendet werden kann.Das ist es, was zsh versucht, und selbst wenn es viel Arbeit in diese Richtung geleistet hat,
schlägt es in einigen Fällen immer noch fehl. Arrays werden (noch) nicht kopiert.
In unserer Sprache verwenden wir für jede Idee einen Namen. Jede neue Idee, die sich radikal von früheren Ideen unterscheidet, muss einen eigenen Namen haben. In unserer Sprache ist es selbstverständlich, dass wir neue Wörter für neue Ideen akzeptieren. Denken Sie an den Namen "Internet", einen neuen Namen für eine neue Idee. Die Verwendung alter Namen für neue Konzepte führt immer zu Verwirrung und Missverständnissen. So werden wir Menschen gebaut und es klingt vernünftig, wenn wir darüber nachdenken.
Von Anfang an verwendete eine Variable in einer Shell einen Namen (angenommen
a
) und ihr Wert war das Ergebnis der Erweiterung (einer Shell-Prozedur) der Symbole$a
. Eine Variable konnte eine Zeichenfolge oder eine Zahl enthalten (automatisch konvertiert).Das ist genau das, was Perl hat mit der Verwendung
@a
zu bezeichnen , eine Liste , während hält$a
füran scalar
.Das heißt: Die beiden oben genannten werden als SCALAR- und LIST-Kontext bezeichnet. ( In Perl ).
Aus diesem Grund müssen wir
a=(1 2)
ein Array zuweisen (es gibt auch andere Möglichkeiten, aber dies ist die häufigste). Eine Syntax, die in früheren Shells ungültig ist.In sh (Strich in diesem Fall):
Und die Erweiterung der Variablen wurde von
$a
oder entsprechend${a}
der neuen Syntax erweitert (und in sh ungültig) `$ {a [@]}:In einfacheren Schalen (in diesem Fall Asche):
Es ist bedauerlich, dass solch eine verschlungene Art des Schreibens eines Arrays gewählt wurde.
Wenn ich eine neue Syntax vorschlagen würde, würde ich wahrscheinlich dem Perl-Beispiel folgen und etwas Ähnliches wie "@a" für eine Liste von Werten verwenden (oder vielleicht
#a
oder%a
). Das sollte Gegenstand einiger Diskussionen sein, um einen Konsens zu erzielen.${a[@]}
.Kurz gesagt: Aus Gründen der Abwärtskompatibilität wird erwartet, dass die Erweiterung einer einfachen Variablen
$a
nur zu einem Wert führt, nicht zu einer Liste von Werten, sondern zu getrennten Werten.Da in POSIX keine Arrays definiert sind, kann es unwirksam sein, zu zitieren, dass in POSIX die Definition der Parametererweiterung lautet :
Und tatsächlich drucken die meisten Muscheln nur einen Skalar mit
$a
Für das Schreiben von Shell-Skripten ist zsh [a] also die ungerade.
Getestet mit diesem Skript:
[a] Auch yash. csh-kompatible Shells nicht getestet. Skripte mit csh sind ein bekanntes Problem.
quelle