Ich werde versuchen, dies zu erklären, aber verzeihen Sie mir, wenn ich dem von Ihnen angegebenen Beispiel nicht folge. Ich werde lieber versuchen, Sie auf meinem eigenen, anderen Ansatz zu führen.
Sie sagen, Sie verstehen bereits Konzepte wie „Variablen“ und „Erweitern“ usw., sodass ich nur einige Hintergrundkenntnisse überfliege, die andernfalls einen tieferen Fokus erfordern würden.
Zunächst möchte ich sagen, dass der Befehl auf seiner grundlegendsten Ebene declare
nur eine Möglichkeit ist, Bash mitzuteilen, dass Sie einen variablen Wert benötigen (dh einen Wert, der sich während der Skriptausführung möglicherweise ändert) und auf den Sie verweisen Dieser Wert verwendet einen bestimmten Namen, genau den Namen, den Sie neben dem angebendeclare
Befehl selbst .
Das ist:
declare foo="bar"
teilt Bash mit, dass die benannte Variable foo
den Wert haben sollbar
.
Aber ... Moment mal ... wir können das tun, ohne es zu benutzen declare
, oder? Wie in:
foo="bar"
Sehr richtig.
Nun, es kommt vor, dass die obige einfache Zuordnung tatsächlich implizit ist Weg eine Variable zu deklarieren.
( Es kommt auch vor, dass das Obige eine der wenigen Möglichkeiten ist, den Wert der genannten Variablen zu ändern. In der foo
Tat ist es genau der direkteste, präziseste, offensichtlichste und direkteste Weg. Aber es ist nicht der einzige. Ich werde später darauf zurückkommen. ).
Aber dann, wenn es so gut möglich ist, einen "Namen, der der Kürze halber Variablenwerte markiert" (der Kürze halber nur "Variable") zu verwenden, ohne ihn zu verwenden declare
deklarieren, warum sollten Sie diese pompöse "Deklaration" niemals verwenden? " Befehl ?
Die Antwort liegt in der Tatsache, dass die oben implizite Art, eine Variable zu deklarieren (foo="bar"
) , implizit dazu führt, dass Bash die Variable des Typs betrachtet, der im typischen Verwendungsszenario für eine Shell am häufigsten verwendet wird.
Ein solcher Typ ist der Zeichenfolgentyp, dh eine Folge von Zeichen ohne besondere Bedeutung. Daher erhalten Sie eine Zeichenfolge, wenn Sie die implizite Deklaration verwenden.
Aber Sie als Programmierer müssen manchmal lieber eine Variable als z. B. eine Zahl betrachten, für die Sie arithmetische Operationen ausführen müssen. Wenn Sie eine implizite Deklaration wie verwenden, foo=5+6
wird Bash nicht den Wert 11 zuweisen, foo
wie Sie möchten erwarten von. Es wird eher foo
der Reihenfolge der drei Zeichen zugewiesen 5
+
6
.
Also ... Sie müssen Bash mitteilen, dass Sie foo
als Zahl und nicht als Zeichenfolge betrachtet werden möchten . Dafür ist eine explizite Funktion declare
nützlich.
Sag nur:
declare -i foo=5+6 # <<- note the '-i' option: it means 'integer'
und Bash erledigt gerne die Mathematik für Sie und weist der Variablen den numerischen Wert 11 zu foo
.
Das heißt: Indem declare -i foo
Sie sagen, dass Sie der Variablen foo
das Attribut einer Ganzzahl geben.
Das Deklarieren von Zahlen (genau ganze Zahlen, da Bash Dezimalstellen, Gleitkommawerte und all das immer noch nicht versteht) ist möglicherweise der erste Grund für die Verwendung declare
, aber nicht der einzige Grund. Wie Sie bereits verstanden haben, können Sie Variablen einige andere Attribute zuweisen. Zum Beispiel können Sie Bash verwenden, um den Wert einer Variablen immer in Großbuchstaben zu schreiben, egal was passiert: Wenn Sie sagen declare -u foo
, dann, wenn Sie sagen , dass foo=bar
Bash BAR
der Variablen tatsächlich die Zeichenfolge zuweistfoo
.
Um einer Variablen eines dieser Attribute zuzuweisen, müssen Sie den declare
Befehl verwenden. Es gibt keine andere Wahl.
Eines der Attribute, die Sie durchgeben können, declare
ist das berüchtigte "Name-Ref" -n
-Attribut , das Attribut. ( Und jetzt werde ich das Konzept wieder aufnehmen, das ich zuvor auf Eis gelegt habe ).
Das Attribut name-ref ermöglicht Bash-Programmierern grundsätzlich eine andere Möglichkeit, den Wert einer Variablen zu ändern. Genauer gesagt gibt es einen indirekten Weg, dies zu tun.
So funktioniert es:
Sie haben declare
eine Variable mit dem -n
Attribut, und es wird sehr empfohlen (obwohl dies nicht unbedingt erforderlich ist, aber es vereinfacht die Sache), dass Sie dieser Variablen mit demselben declare
Befehl auch einen Wert geben . So was:
declare -n baz="foo"
Dies teilt Bash mit, dass von nun an jedes Mal, wenn Sie den Wert der genannten Variablen verwenden oder ändern, der Wert der genannten Variablen baz
tatsächlich verwendet oder geändert wird foo
.
Was bedeutet, dass Sie von nun an so etwas wie sagen können baz=10+3
, foo
um den Wert 13 zu erhalten. Angenommen, dies foo
wurde zuvor declare -i
wie vor einer Minute als integer ( ) deklariert , andernfalls wird die Sequenz der vier erhalten Zeichen 1
0
+
3
.
Außerdem: Wenn Sie foo
den Wert direkt ändern , wie in foo=15
, sehen Sie 15 auch, indem Sie sagen echo “${baz}”
. Dies liegt daran, dass baz
die als name-ref von deklarierte Variable foo
immer foo
den Wert widerspiegelt .
Der obige declare -n
Befehl wird als "Namensreferenz" bezeichnet, da sich die Variable baz
auf den Namen einer anderen Variablen bezieht . Tatsächlich haben wir angegeben, baz
dass der Wert "foo" ist, der aufgrund der -n
Option von Bash als Name für eine andere Variable behandelt wird.
Nun, warum auf der Erde würden Sie jemals das tun wollen?
Nun ... es ist erwähnenswert, dass dies eine Funktion für ziemlich fortgeschrittene Bedürfnisse ist.
In der Tat so weit fortgeschritten, dass, wenn ein Programmierer mit einem Problem konfrontiert ist, das wirklich eine Namensreferenz erfordern würde, es wahrscheinlich ist, dass ein solches Problem eher durch die Verwendung einer geeigneten Programmiersprache anstelle von Bash behoben werden sollte.
Einer dieser erweiterten Bedürfnisse ist, zum Beispiel, wenn Sie als Programmierer, können nicht während der Entwicklung wissen , welche Variablen , die Sie in einem bestimmten Punkt eines Skripts verwenden, aber es wird voll dynamisch zur Laufzeit bekannt sein. Und da es für einen Programmierer nicht möglich ist, zur Laufzeit einzugreifen, besteht die einzige Möglichkeit darin, im Voraus eine solche Situation im Skript vorzusehen, und ein „Namensreferenz“ kann der einzig praktikable Weg sein. Denken Sie beispielsweise als Plug-Ins an einen Plug-Ins. Der Programmierer eines "Plugin-fähigen" Programms muss im Voraus generische Vorkehrungen für zukünftige (und möglicherweise Drittanbieter-) Plug-Ins treffen. Daher muss der Programmierer Einrichtungen wie eine Namensreferenz in Bash verwenden.
Eine andere erweiterte Notwendigkeit ist , wenn Sie mit großen Datenmengen zu tun haben , in RAM und Sie auch , dass die Daten um Funktionen des Skripts übergeben müssen , dass auch , dass die Daten auf dem Weg ändern müssen. In einem solchen Fall könnten Sie diese Daten sicherlich von einer Funktion in eine andere kopieren (wie es Bash tut, wenn Sie dies tun dest_var="${src_var}"
oder wenn Sie Funktionen wie in aufrufen myfunc "${src_var}"
), aber wenn diese Daten eine große Menge sind, würde dies zu einer enormen Verschwendung von RAM und zu einer sehr großen Menge führen ineffizienter Betrieb. In solchen Situationen besteht die Lösung darin, keine Kopie der Daten, sondern eine Referenz zu verwendenzu diesen Daten. In Bash ein Namensreferenz. Dieser Anwendungsfall ist in jeder modernen Programmiersprache die Norm, aber in Bezug auf Bash ist er ziemlich außergewöhnlich, da Bash hauptsächlich für kurze einfache Skripte entwickelt wurde, die sich hauptsächlich mit Dateien und externen Befehlen befassen, und daher Bash-Skripte selten sehr umfangreich sein müssen Datenmenge zwischen Funktionen. Und wenn die Funktionen eines Skripts einige Daten gemeinsam nutzen müssen (darauf zugreifen und sie ändern müssen), wird dies normalerweise durch die Verwendung einer globalen Variablen erreicht, was in Bash-Skripten durchaus üblich ist, da es in den richtigen Programmiersprachen sehr veraltet ist.
Dann kann es einen bemerkenswerten Anwendungsfall für Namensreferenzen in Bash geben, der (möglicherweise ironischerweise) damit verbunden ist, wenn Sie noch andere Arten von Variablen verwenden:
- Variablen, die als "indizierte Arrays" deklariert sind (
declare -a
)
- Variablen, die als "assoziative Arrays" deklariert sind (
declare -A
).
Hierbei handelt es sich um eine Art von Variablen, die möglicherweise einfacher (und auch effizienter) an Funktionen weitergegeben werden können, indem Namensreferenzen anstelle von normalem Kopieren verwendet werden, selbst wenn sie keine großen Datenmengen enthalten.
Wenn all diese Beispiele seltsam und immer noch unverständlich klingen, dann nur, weil Namensreferenzen in der Tat ein fortgeschrittenes Thema sind und das typische Verwendungsszenario von Bash selten benötigt wird.
Ich könnte Ihnen von Gelegenheiten erzählen, bei denen ich in Bash Verwendung für Namensreferenzen gefunden habe, aber bisher waren sie hauptsächlich für ziemlich „esoterische“ und komplizierte Bedürfnisse gedacht, und ich befürchte, dass ich sie nur beschreiben würde, wenn ich sie beschreiben würde Kompliziere die Dinge für dich an diesem Punkt deines Lernens. Um nur das am wenigsten komplexe (und möglicherweise nicht esoterische) zu erwähnen: Rückgabe von Werten aus Funktionen. Bash unterstützt diese Funktionalität nicht wirklich, daher habe ich das gleiche mithilfe von Namensreferenzen erhalten. Dies ist übrigens genau das, was Ihr Beispielcode tut.
Außerdem ein kleiner persönlicher Rat, der eigentlich besser für einen Kommentar geeignet wäre, aber ich konnte ihn nicht genug verdichten, um in die Grenzen des Kommentars von StackExchange zu passen.
Ich denke, das Beste, was Sie im Moment tun sollten, ist, einfach mit Namensreferenzen zu experimentieren, indem Sie die einfachen Beispiele verwenden, die ich gezeigt habe, und vielleicht mit dem von Ihnen bereitgestellten Beispielcode, wobei Sie den Teil „Warum um alles in der Welt“ für den Moment außer Acht lassen und sich nur darauf konzentrieren der Teil "wie es funktioniert". Wenn Sie ein wenig experimentieren, kann der „Wie“ -Teil besser in Ihren Kopf eindringen, sodass Ihnen der „Warum“ -Teil zu gegebener Zeit klar wird, wenn (oder wenn) Sie ein echtes praktisches Problem haben, für das ein Name- ref wäre wirklich nützlich.