Was ist die Datenstruktur von $ @ in der Shell?

13

Wir verwenden normalerweise $@alle Argumente mit Ausnahme von $ 0. Ich weiß jedoch nicht, was Datenstruktur $@ist.

Kann $*mir jemand eine Erklärung auf Dolmetscherebene geben, warum es sich anders verhält, wenn es in doppelte Anführungszeichen eingeschlossen wird?

Es kann in der for-Schleife iteriert werden, es scheint also ein Array zu sein. Es kann aber auch ganz einfach wiedergegeben werden echo $@, wenn es sich um ein Array handelt, nur das erste Element wird angezeigt. Aufgrund der Einschränkung der Shell kann ich keinen weiteren Testcode schreiben, um ihn auszuführen.

Unterschied zwischen diesem Beitrag : Dieser Beitrag zeigt, wie $@sich anders verhält als $*. Aber ich frage mich über den Datentyp von $@. Shell als interpretierende Sprache sollte wie Python Daten nach einer Reihe grundlegender Typen darstellen. Oder mit anderen Worten, ich möchte wissen, wie $ @ im Computerspeicher gespeichert ist.

Ist es eine Zeichenfolge, eine mehrzeilige Zeichenfolge oder ein Array?

Wenn es sich um einen eindeutigen Datentyp handelt, kann eine benutzerdefinierte Variable als Instanz dieses Typs definiert werden?

Davmos
quelle
1
Mögliches Duplikat von Was ist der Unterschied zwischen $ * und $ @?
Haxiel
@Haxiel, ich glaube nicht, ich habe den Unterschied unten in meinem Beitrag geschrieben.
Davmos
Sie sollten den Unterschied in der Ausgabe besser mit printf '%s\n' "$@"und testen printf '%s\n' "$*". Das echoDienstprogramm gibt nur die Argumente aus, unabhängig davon, ob es sich um eines oder mehrere handelt. Beide sind Arrays (von Strings), aber sie verhalten sich unterschiedlich, wenn sie in doppelte Anführungszeichen gesetzt werden. Wenn eine der beiden Zeichenfolgen mehrzeilig wäre, könnten sie keine mehrzeiligen Zeichenfolgen speichern (was auch möglich ist). Es ist unklar, welches Problem Sie lösen möchten.
Kusalananda
2
Ihre Frage entspricht der Frage, was eine @varVariable in Perl in Bezug auf den zugrunde liegenden Speicher darstellt. Aus der Sicht eines gewöhnlichen Perl-Programms spielt es keine Rolle, außer dass es als Array / Liste zugänglich ist (und die Tatsache, dass es Kontexte gibt, in denen eine Liste erwartet wird).
Kusalananda

Antworten:

16

Das begann als Hack in der Bourne-Shell. In der Bourne-Shell wurde die IFS-Wortteilung (nach der Tokenisierung) für alle Wörter im Listenkontext (Befehlszeilenargumente oder die Wörter, auf denen sich die forSchleife befindet) durchgeführt. Wenn du hättest:

IFS=i var=file2.txt
edit file.txt $var

Diese zweite Linie in 3 Worten tokengekennzeichneten würde, $varerweitert werden würde, und Split + glob würde auf allen drei Worte geschehen, so würde Sie am Ende läuft edmit t, f, le.txt, f, le2.txtals Argumente.

Teile davon zu zitieren würde den Split + Glob verhindern. Die Bourne-Shell erinnerte sich anfangs daran, welche Zeichen in Anführungszeichen gesetzt wurden, indem sie das 8. Bit intern setzte (das änderte sich später, als Unix 8-Bit-sauber wurde, aber die Shell erinnerte sich noch an das angeführte Byte).

Beides $*und $@war die Verkettung der Positionsparameter mit dem Zwischenraum. Aber es gab eine spezielle Verarbeitung von $@Anführungszeichen. Wenn $1enthalten foo barund $2enthalten baz, "$@"würde erweitern auf:

foo bar baz
^^^^^^^ ^^^

(wobei das ^s oben angibt, für welches der Zeichen das 8. Bit gesetzt ist). Wobei das erste Leerzeichen in Anführungszeichen gesetzt wurde (wobei das 8. Bit gesetzt war), aber nicht das zweite (dasjenige, das zwischen den Wörtern hinzugefügt wurde).

Und es ist die IFS-Aufteilung, die sich um die Trennung der Argumente kümmert (vorausgesetzt, das Leerzeichen ist so, $IFSwie es standardmäßig ist). Das ist ähnlich wie $*bei seinem Vorgänger die Mashey-Shell (selbst basierend auf der Thomson-Shell, während die Bourne-Shell von Grund auf neu geschrieben wurde) erweitert wurde.

Das erklärt, warum in der Bourne-Shell anfangs "$@"die leere Zeichenfolge statt gar nichts erweitert wurde, wenn die Liste der Positionsparameter leer war (Sie mussten damit umgehen ${1+"$@"}), warum die leeren Positionsparameter nicht "$@"beibehalten wurden und warum nicht $IFSfunktioniert nicht, wenn das Leerzeichen nicht enthalten ist.

Die Absicht war, die Liste der Argumente wörtlich an einen anderen Befehl weitergeben zu können, aber das funktionierte nicht richtig für die leere Liste, für leere Elemente oder wenn $IFSkein Leerzeichen vorhanden war (die ersten beiden Probleme wurden in späteren Versionen behoben) ).

Die Korn-Shell (auf der die POSIX-Spezifikation basiert) hat dieses Verhalten auf verschiedene Arten geändert:

  • Die IFS-Aufteilung wird nur für das Ergebnis nicht zitierter Erweiterungen durchgeführt (nicht für wörtliche Wörter wie editoder file.txtim obigen Beispiel).
  • $*und $@sind mit dem ersten Zeichen verbunden ist $IFSoder , wenn Raum $IFSist leer , außer dass für eine zitiert "$@"wird , dass wie in dem Tischler Bourne - Shell unquoted und für eine zitiert , "$*"wenn IFSleer ist , die Positionsparameter werden ohne Trennangehängt.
  • es zusätzliche Unterstützung für Arrays und mit ${array[@]} ${array[*]}erinnerte an Bourne $*und $@aber indice 0 anstelle von 1 beginnt und sparse (mehr wie assoziative Arrays) , die mittels $@nicht wirklich als KSH Array behandelt werden kann (vergleiche mit csh/ rc/ zsh/ fish/ yashwo $argv/ $*sind normale Arrays).
  • Die leeren Elemente bleiben erhalten.
  • "$@"Wenn $#0 ist, wird statt der leeren Zeichenfolge jetzt nichts mehr angezeigt. Funktioniert, "$@"wenn $IFSkeine Leerzeichen enthalten sind, es sei denn, wenn IFSleer ist. Ein nicht in Anführungszeichen gesetztes $*Argument ohne Platzhalterzeichen wird zu einem Argument erweitert (wobei die Positionsparameter mit einem Leerzeichen verbunden werden), wenn $IFSes leer ist.

ksh93 hat die verbleibenden Probleme oben behoben. In ksh93 $*und wird $@die Liste der Positionsparameter erweitert, unabhängig vom Wert von getrennt $IFSund dann in Listenkontexten weiter aufgeteilt + globbed + geschweift-expandiert, $*verbunden mit dem ersten Byte (nicht dem Zeichen) von $IFS, "$@"in Listenkontexten wird die Liste erweitert von Positionsparametern, unabhängig vom Wert von $IFS. In Nicht-Listenkontext wie in var=$@, $@mit Raum verbunden , unabhängig vom Wert von $IFS.

bashDie Arrays sind nach den ksh-Arrays entworfen. Die Unterschiede sind:

  • Keine Klammererweiterung bei nicht zitierter Erweiterung
  • erstes Zeichen von $IFSanstelle von für Byte
  • Einige Unterschiede in der Groß- und Kleinschreibung, wie z. B. die Erweiterung von, $*wenn im Kontext, in dem keine Liste vorhanden $IFSist, keine Anführungszeichen vorhanden sind, wenn leer.

Während die POSIX-Spezifikation früher ziemlich vage war, gibt sie jetzt mehr oder weniger das Bash-Verhalten an.

Es unterscheidet sich von normalen Arrays darin kshoder bashdarin:

  • Indizes beginnen bei 1 anstelle von 0 (außer bei "${@:0}"Includes $0(kein Positionsparameter, und bei Funktionen wird der Name der Funktion angegeben oder nicht, abhängig von der Shell und der Definition der Funktion)).
  • Sie können Elemente nicht einzeln zuweisen
  • Es ist nicht spärlich, Sie können Elemente nicht einzeln entfernen
  • shift kann verwendet werden.

In zshoder yashwo Arrays normale Arrays sind (nicht spärlich, Indizes beginnen wie in allen anderen Shells mit Ausnahme von ksh / bash bei einem), $*wird als normales Array behandelt. zshhat $argvals Alias ​​dafür (aus Kompatibilitätsgründen mit csh). $*ist dasselbe wie $argvoder ${argv[*]}(Argumente, die mit dem ersten Zeichen von verbunden sind, $IFSaber dennoch in Listenkontexten getrennt sind). "$@"mag "${argv[@]}"oder "${*[@]}"}unterzieht sich der Korn-artigen Sonderverarbeitung.

Stéphane Chazelas
quelle
8

Ich weiß jedoch nicht, was Datenstruktur $@ist.

Es ist ein spezieller Parameter, der sich zu den Werten der Positionsparameter erweitert ... Aber das ist keine Frage der Terminologie.

Wir können die Positionsparameter als Teile von betrachten $@, so dass sie eine Reihe von unterschiedlichen Elementen ( $1, $2...) haben, auf die unabhängig zugegriffen werden kann und die durch aufeinanderfolgende natürliche Zahlen benannt sind. Das macht es zu etwas, das normalerweise als Array bezeichnet wird.

Die Syntax ist allerdings etwas seltsam und sogar begrenzt. Es gibt keine Möglichkeit, ein einzelnes Element des Arrays einzeln zu ändern. Stattdessen muss das Ganze auf einmal eingestellt werden. (Sie können set -- "$@" fooeinen Wert anhängen oder set -- "${@:1:2}" foo "${@:3}"einen Wert in der Mitte hinzufügen. In beiden Fällen müssen Sie jedoch die gesamte resultierende Liste ausschreiben.)

Warum es sich anders verhält, $*wenn es in doppelte Anführungszeichen eingeschlossen wird,

Weil sie so definiert sind, dass sie sich anders verhalten.

Es kann aber auch ganz einfach wiedergegeben werden echo $@, wenn es sich um ein Array handelt, nur das erste Element wird angezeigt.

Wenn Sie die Tatsache meinen, dass a=(foo bar asdf); echo $anur ausgegeben wird foo, dann handelt es sich meistens um eine Eigenart der Shell-Syntax und die Tatsache, dass benannte Arrays im ksh-Stil später als die Positionsparameter und erstellt wurden $@. Einfach $aist das Gleiche, ${a[0]}also hat es die abwärtskompatible Bedeutung eines einzelnen Skalarwerts, unabhängig davon, ob aes sich um ein Array oder eine einfache Skalarvariable handelt.

Das @Zeichen, das sich auf die gesamte Liste bezieht, wurde mit benannten Arrays wiederverwendet "${a[@]}", um die gesamte Liste zu erhalten. Im Vergleich zu benannten Arrays mit $@werden die unnötigen Klammern und der Name einfach übersprungen.

Oder mit anderen Worten, ich möchte wissen, wie $@im Computerspeicher gespeichert.

Das hängt von der Implementierung ab. Sie müssen sich den Quellcode einer bestimmten Shell ansehen, die Sie interessiert.

Ist es eine Zeichenfolge, eine mehrzeilige Zeichenfolge oder ein Array?

Meistens ein Array. Obwohl sie sich von den ksh-artigen benannten Arrays unterscheiden, können sie beliebige nichtnegative Ganzzahlen als Indizes haben, nicht nur aufeinanderfolgende wie bei $@. (Das heißt, kann ein benanntes Array spärlich sein, und hat zum Beispiel des Indizes 1, 3und 4mit 0und 2fehlt. Das ist nicht möglich , mit den Positionsparametern.)

Es ist keine einzelne Zeichenfolge, da sie zu verschiedenen Elementen erweitert werden kann, und das Aufrufen der $@Zeilenelemente ist auch nicht richtig, da jede reguläre Variable oder einer der Positionsparameter (Elemente von ) auch Zeilenumbrüche enthalten kann.

Wenn es sich um einen eindeutigen Datentyp handelt, kann eine benutzerdefinierte Variable als Instanz dieses Typs definiert werden?

Nein. Aber benannte Arrays sind wahrscheinlich sowieso nützlicher.

ilkkachu
quelle
1
+1. TL: DR $@ist keine Datenstruktur, sondern eine von wenigen Funktionen / Operatoren zum Erweitern der Positionsparameter-Datenstruktur.
Peter Cordes