Nachdem ich ilkkachus Antwort auf diese Frage gelesen hatte, erfuhr ich von der Existenz der eingebauten declare
(mit Argument -n
) Shell.
help declare
bringt:
Legen Sie Variablenwerte und Attribute fest.
Deklarieren Sie Variablen und geben Sie ihnen Attribute. Wenn keine NAMEs angegeben sind, zeigen Sie die Attribute und Werte aller Variablen an.
-n ... macht NAME zu einem Verweis auf die Variable, die durch ihren Wert benannt ist
Ich bitte um eine allgemeine Erklärung mit einem Beispiel bezüglich, declare
weil ich das nicht verstehe man
. Ich weiß, was eine Variable ist und erweitere sie, aber ich vermisse immer noch das man
on declare
(variable Attribut?).
Vielleicht möchten Sie dies anhand des Codes von ilkkachu in der Antwort erklären:
#!/bin/bash
function read_and_verify {
read -p "Please enter value for '$1': " tmp1
read -p "Please repeat the value to verify: " tmp2
if [ "$tmp1" != "$tmp2" ]; then
echo "Values unmatched. Please try again."; return 2
else
declare -n ref="$1"
ref=$tmp1
fi
}
Antworten:
In den meisten Fällen reicht es mit einer impliziten Deklaration in
bash
Manchmal möchten Sie jedoch, dass der Wert einer Variablen nur eine Ganzzahl ist (für den Fall, dass er sich später sogar automatisch ändert, kann er nur in eine Ganzzahl geändert werden, in einigen Fällen standardmäßig auf Null) und kann Folgendes verwenden:
oder
Manchmal möchten Sie Arrays, und dann brauchen Sie
declare
oder
Gute Tutorials zu Arrays finden
bash
Sie beispielsweise, wenn Sie im Internet mit der Suchzeichenfolge 'bash array tutorial' (ohne Anführungszeichen) surfenlinuxconfig.org/how-to-use-arrays-in-bash-script
Ich denke, dies sind die häufigsten Fälle, wenn Sie Variablen deklarieren.
Bitte beachten Sie auch, dass
declare
die Variable lokal (in der Funktion)ohne Namen werden alle Variablen aufgelistet (in der aktiven Shell)
Schließlich erhalten Sie eine kurze Zusammenfassung der Funktionen des
declare
inbash
den Befehl integrierten Shell- Befehlsquelle
user:muru
gestimmt wurde, die Bearbeitung abzulehnen. Bitte beachten Sie, dass ich und Muru in den verschiedenen Abschnitten dieser Website mehrmals "zusammengestoßen" sind und ich davon ausgehe, dass seine Ablehnung nicht objektiv ist und im Gegensatz zu den direkten Änderungen, die auf der Seite mit den Bearbeitungsvorschlägen hier vorgestellt werden, tatsächlich schädlich ist: unix.stackexchange. com / review / suggested-edits / 325749Die Ausgabe von
help declare
ist ziemlich knapp. Eine klarere Erklärung findet sich inman bash
oderinfo bash
- letztere ist die Quelle für das Folgende.Zunächst einige Definitionen. Informationen zu Variablen und Attributen :
Und über das
declare
eingebaute :Beachten Sie, dass Namensreferenzvariablen nur in Bash 4.3 oder höher verfügbar sind 1 .
Auch für eine nützliche Einführung in
declare
und Variablenattribute in Bash möchte ich Sie auf diese Antwort auf "Was tundeclare name
unddeclare -g
tun?" Verweisen (die sich jedoch hauptsächlich auf den Umfang der Variablen konzentriert).Grundsätzlich 2 ,
declare name=[value]
entspricht der Zuordnungname=[value]
Sie wahrscheinlich vertraut sind. In beiden Fällenname
wird der Nullwert zugewiesen, wenn ervalue
fehlt.Beachten Sie, dass dies etwas anders
declare name
ist festgelegt die Variablename
3 :Somit kann die Variable
name
sein:declare name
;name=
oderdeclare name=
;name=value
oderdeclare name=value
.Allgemeiner,
declare [options] name=value
name
- dies ist ein Parameter mit einem Namen, der wiederum nur ein Teil des Speichers ist, den Sie zum Speichern von Informationen verwenden können 4 ;value
zu;name
die Attribute festgelegt werden, die sowohl die Art des Werts definieren, den gespeichert werden können (nicht in Bezug auf einen Typ) streng genommen , da Bashs Sprache nicht typisiert ist), als auch die Art und Weise, wie er manipuliert werden kann.Attribute lassen sich wahrscheinlich anhand eines Beispiels leichter erklären: using
declare -i name
setzt das Attribut "integer" vonname
und lässt es als Ganzzahl behandelt werden. unter Angabe des Handbuchs "wird eine arithmetische Auswertung durchgeführt, wenn der Variablen ein Wert zugewiesen wird":In Anbetracht des oben Gesagten geschieht in ilkkachus Code Folgendes:
Eine Variable
ref
mit dem Namen "nameref" und dem Inhalt von wird deklariert$1
(dem ersten Positionsargument) wird ihr zugewiesen:Das Ziel einer Namensreferenzvariablen
ref
ist es, den Namen einer anderen Variablen zu speichern, die im Allgemeinen nicht im Voraus bekannt ist, möglicherweise weil wir möchten, dass sie dynamisch definiert wird (z. B. weil wir einen Code wiederverwenden und haben möchten angewendet auf mehrere Variablen) und um eine bequeme Möglichkeit zum Verweisen (und Manipulieren) darauf bereitzustellen. (Nicht die einzige: Indirektion ist eine Alternative; siehe Shell-Parametererweiterung ).Wenn der Wert der Variablen
tmp1
zugewiesen istref
:Eine zusätzliche Variable, deren Name der Wert von ist
ref
, wird implizit deklariert. Der Wert vontmp1
wird auch indirekt der implizit deklarierten Variablen durch diese explizite Zuordnung zu zugewiesenref
.Rufen Sie im Kontext Ihrer verknüpften Frage
read_and_verify
als aufdeklariert die Variable
domain
und weist ihr den Wert vontmp1
(dh die Benutzereingabe) zu. Es wurde genau entwickelt, um den Code, der mit dem Benutzer interagiert, wiederzuverwenden und eine nameref-Variable zum Deklarierendomain
und einige andere Variablen zu nutzen.Um den impliziten Teil genauer zu betrachten, können wir den Prozess Schritt für Schritt reproduzieren:
1 Referenz: CHANGES- Datei, Abschnitt "3. Neue Funktionen in Bash", Punkt "w".
Dies kann relevant sein: zum Beispiel CentOS Linux 7.6 (derzeit die neueste Version) mit Bash 4.2 ausgeliefert .
2 Wie bei Shell-Builtins üblich, eine erschöpfende und Erklärung schwer zu finden, da sie verschiedene, möglicherweise heterogene Aktionen ausführen. Ich werde mich darauf konzentrieren, nur Attribute zu deklarieren, zuzuweisen und festzulegen, und ich werde in Betracht ziehen, Attribute als außerhalb des Rahmens dieser Antwort aufzulisten, zu erfassen und zu entfernen.
3 Dieses Verhalten von
declare -p
wurde in Bash 4.4 eingeführt. Referenz: Datei CHANGES , Abschnitt "3. Neue Funktionen in Bash", Punkt "f".Wie G-Man in den Kommentaren hervorhob,
declare name; declare -p name
liefert Bash 4.3 einen Fehler. Sie können jedoch weiterhin überprüfen, ob diesname
vorhanden istdeclare -p | grep 'declare -- name'
.4 FullBashGuide, Parameter auf mywiki.wooledge.org
quelle
declare name
gefolgt von den Ergebnissendeclare -p name
"bash: declare: name: not found". (Obwohldeclare -p | grep na
Ausbeutendeclare -- name
.) (2) Ich glaube, dass es ein wenig irreführend ist,echo "${name-isunset}"
im Kontext vondeclare name
darzustellen, da eine nicht deklarierte (dh nicht definierte) Variable genauso behandelt wird wie eine deklarierte, aber nicht gesetzte Variable. (3) Möglicherweise möchten Sie erwähnen, dass namerefs nur in Bash-Version 4.3 und höher verfügbar sind.GNU bash, version 5.0.7(1)-release (x86_64-pc-linux-gnu)
betrifft, liefert mein unter Arch Linux immer noch die Ergebnisse, die ich gezeigt habe. Vielleicht wurde dieses Verhalten erst kürzlich eingeführt, das werde ich untersuchen.declare x
dies nicht der Fall istx
, während dies derdeclare x=
Fall ist. Ich konnte keinen Hinweis finden, der die Behauptung stützt, dassdeclare -- x
(als Ausgabe vondeclare -p x
) "nicht gesetzt"declare -- x=""
bedeutet , während "gesetzt" bedeutet; Daher habe ich die${parameter-word}
Erweiterung eingeführt, auch wenn sie nicht zwischen "nicht gesetzt" und "nicht vorhanden" unterscheiden kann, wie Sie betonen. Ich bin mir jedoch nicht sicher, wie ich dies in meiner Antwort klarstellen kann (ohne den Leser vom Punkt abzulenken).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:
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: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 eherfoo
der Reihenfolge der drei Zeichen zugewiesen5
+
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 Funktiondeclare
nützlich.Sag nur:
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 Variablenfoo
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 sagendeclare -u foo
, dann, wenn Sie sagen , dassfoo=bar
BashBAR
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 demselbendeclare
Befehl auch einen Wert geben . So was: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 wirdfoo
.Was bedeutet, dass Sie von nun an so etwas wie sagen können
baz=10+3
,foo
um den Wert 13 zu erhalten. Angenommen, diesfoo
wurde zuvordeclare -i
wie vor einer Minute als integer ( ) deklariert , andernfalls wird die Sequenz der vier erhalten Zeichen1
0
+
3
.Außerdem: Wenn Sie
foo
den Wert direkt ändern , wie infoo=15
, sehen Sie 15 auch, indem Sie sagenecho “${baz}”
. Dies liegt daran, dassbaz
die als name-ref von deklarierte Variablefoo
immerfoo
den Wert widerspiegelt .Der obige
declare -n
Befehl wird als "Namensreferenz" bezeichnet, da sich die Variablebaz
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 aufrufenmyfunc "${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:
declare -a
)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.
quelle
declare
von der spezifischen Erklärung von-n
aus dem persönlichen Nachtrag abzugrenzen . Ich habe auch hier und da ein bisschen umformuliert, aber nichts Wesentliches, also denke ich, dass Sie den-n
Teil und den kleinen Nachtrag einfach noch einmal lesen können .foo="bar"
unddeclare foo="bar"
. Möglicherweise möchten Sie (wenn auch nur in einer Fußnote) erwähnen, dass in einer Shell-Funktiondeclare foo="bar"
eine lokale Variable undfoo="bar"
eine globale Variable erstellt werden.Im Allgemeinen werden
declare
in derbash
Shell Attribute für Variablen festgelegt (oder entfernt oder angezeigt) . Ein Attribut ist eine Art Annotation, die besagt: "Dies ist eine Namensreferenz" oder "Dies ist ein assoziatives Array" oder "Diese Variable sollte immer als Ganzzahl ausgewertet werden" oder "Diese Variable ist schreibgeschützt und kann nicht." zurückgesetzt werden "oder" diese Variable wird exportiert (eine Umgebungsvariable) "usw.Das
typeset
eingebaute ist ein Synonym fürdeclare
inbash
, wietypeset
es in anderen Shells (ksh
wo es entstanden ist undzsh
zum Beispiel) zum Festlegen von Variablenattributen verwendet wird.Schauen Sie sich das Namensreferenzbeispiel in der Frage genauer an:
Die Shell-Funktion, die Sie anzeigen, mit einem zusätzlichen Code, der sie verwendet:
Führen Sie dies aus:
Dies zeigt, dass die
foo
Variable auf nichts gesetzt wird, wenn der Benutzer zwei verschiedene Zeichenfolgen eingibt.Dies zeigt, dass die Variable
foo
auf die Zeichenfolge gesetzt wird, die der Benutzer eingegeben hat, als er dieselbe Zeichenfolge zweimal eingegeben hat .Der
$foo
Werthello
im Hauptteil des Skripts wird durch die folgenden Zeilen in der Shell-Funktion ermittelt:Dabei
$tmp1
handelt es sich um diehello
vom Benutzer eingegebene Zeichenfolge und$1
die Zeichenfolge,foo
die in der Befehlszeile der Funktion vom Hauptteil des Skripts übergeben wird.Beachten Sie, dass die
ref
Variable mitdeclare -n
einer Namensreferenzvariablen deklariert wird und dass der Wertfoo
als Wert in dieser Deklaration angegeben wird. Dies bedeutet, dass von diesem Zeitpunkt an, bis die Variable den Gültigkeitsbereich verlässt, jede Verwendung der Variablenref
dieselbe ist wie die Verwendungfoo
. Die Variableref
ist eine Namensreferenzvariable, auf diefoo
an dieser Stelle verwiesen wird.Dies hat zur Folge, dass durch Zuweisen eines Werts zu
ref
, wie in der Zeile nach der Deklaration, der Wert zugewiesen wirdfoo
.Der Wert
hello
ist dann im$foo
Hauptteil des Skripts verfügbar .quelle