it-swarm.com.de

Was ist "deklarieren" in Bash?

Nachdem ich ilkkachus Antwort auf diese Frage gelesen hatte, erfuhr ich von der Existenz des declare (mit Argument -n) Shell eingebaut.

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 zu declare, weil ich das man nicht verstehe. Ich weiß, was eine Variable ist und erweitere sie, aber ich vermisse immer noch das man auf declare (Variablenattribut?).

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
}
12
user149572

In den meisten Fällen reicht eine implizite Deklaration in bash aus

asdf="some text"

Manchmal möchten Sie jedoch, dass der Wert einer Variablen nur eine Ganzzahl ist (falls er sich später ändern sollte, auch automatisch, kann er nur in eine Ganzzahl geändert werden, in einigen Fällen standardmäßig auf Null) und kann Folgendes verwenden:

declare -i num

oder

declare -i num=15

Manchmal möchten Sie Arrays, und dann benötigen Sie declare

declare -a asdf   # indexed type

oder

declare -A asdf   # associative type

Gute Tutorials zu Arrays finden Sie in bash, wenn Sie beispielsweise mit der Suchzeichenfolge 'bash array tutorial' (ohne Anführungszeichen) im Internet surfen

linuxconfig.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

  • in einer Funktion macht declare die Variable lokal (in der Funktion)
  • ohne Namen werden alle Variablen aufgelistet (in der aktiven Shell)

    declare
    

Schließlich erhalten Sie eine kurze Zusammenfassung der Funktionen des in Shell integrierten Befehls declare in bash mit dem Befehl

help declare
4
sudodus

Die Ausgabe von help declare Ist ziemlich knapp. Eine klarere Erklärung finden Sie in man bash Oder info bash - letzteres ist die Quelle für das Folgende.

Zunächst einige Definitionen. Über Variablen und Attribute :

Ein Parameter ist eine Entität, die Werte speichert. ... A Variable ist ein Parameter, der mit einem name bezeichnet wird. Eine Variable hat einen Wert ​​und null oder mehr Attribute. Attribute werden mit dem eingebauten Befehl declare zugewiesen ...

Und über das declareeingebaut :

declare

declare [-aAfFgilnrtux] [-p] [name[=value] …]

Deklarieren Sie Variablen und geben Sie ihnen Attribute. Wenn keine Namen angegeben sind, zeigen Sie stattdessen die Werte der Variablen an.

...

-n
Geben Sie jedem name das nameref Attribut und machen Sie es zu einem Namensverweis auf eine andere Variable. Diese andere Variable wird durch den Wert von Name definiert. Alle Verweise, Zuweisungen und Attributänderungen an Name, mit Ausnahme derjenigen, die das Attribut -n Verwenden oder ändern, werden für die Variable ausgeführt, auf die durch Name 'verwiesen wird. s Wert. ...

Beachten Sie, dass Namensreferenz Variablen nur in Bash 4.3 oder höher verfügbar sind1.

Für eine nützliche Einführung in declare und Variablenattribute in Bash verweise ich Sie auf diese Antwort auf "Was tun declare name Und declare -g ? "(der sich jedoch hauptsächlich auf den Umfang der Variablen konzentriert).


Grundsätzlich2, declare name=[value] Entspricht der Zuordnung name=[value], Mit der Sie wahrscheinlich vertraut sind. In beiden Fällen wird name der Nullwert zugewiesen, wenn value fehlt.

Beachten Sie, dass das etwas andere declare name Stattdessen nicht set ​​die Variable name ist3::

$ declare name

## With the -p option, declare is used to display
## attributes and values of variables
$ declare -p name
declare -- name            ## "name" exists

## Parameter expansion can be used to reveal if a variable is set:
## "isunset" is substituted to "name" only if unset 
$ echo "${name-isunset}"
isunset

Somit kann die Variable name sein:

  • deklariert ​​und nicht gesetzt ​​nach declare name;
  • deklariert ​​und gesetzt ​​mit null als Wert nach name= oder declare name=;
  • deklariert, gesetzt ​​und mit einem nicht null Wert nach name=value oder declare name=value.

Im Allgemeinen declare [options] name=value

  1. erstellt die Variable name - ein Parameter mit einem Namen, der wiederum nur einen Teil des Speichers darstellt, den Sie zum Speichern von Informationen verwenden können4;;
  2. weist ihm den Wert value zu;
  3. legt optional die Attribute von name fest, die sowohl die Art des Werts definieren, den es speichern kann (streng genommen nicht als Typ, da Bashs Sprache nicht typisiert ist), als auch die Art und Weise, wie dies möglich ist manipuliert werden.

Attribute lassen sich wahrscheinlich anhand eines Beispiels leichter erklären: Wenn Sie declare -i name Verwenden, wird das Attribut "integer" von name festgelegt, sodass es als Ganzzahl behandelt werden kann. unter Angabe von manuell "wird eine arithmetische Auswertung durchgeführt, wenn der Variablen ein Wert zugewiesen wird":

## Let's compare an ordinary variable with an integer
$ declare var
$ declare -i int
$ var="1+1"
$ int="1+1"
$ echo "$var"
1+1                 ## The literal "1+1"
$ echo "$int"
2                   ## The result of the evaluation of 1+1

In Anbetracht des oben Gesagten geschieht in ilkkachus Code Folgendes:

  1. Eine Variable mit dem Namen ref wird deklariert, wobei das Attribut "nameref" festgelegt ist, und der Inhalt von $1 (Das erste Positionsargument) wird ihr zugewiesen:

    declare -n ref="$1"
    

    Das Ziel einer Namensreferenzvariablen wie ref besteht darin, 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 a wiederverwenden möchten Stück Code und lassen Sie es auf mehrere Variablen anwenden), und um eine bequeme Möglichkeit zum Verweisen (und Bearbeiten) darauf bereitzustellen. (Nicht die einzige: Indirektion ist eine Alternative; siehe Shell Parameter Expansion ).

  2. Wenn der Wert der Variablen tmp1ref zugewiesen wird:

    ref=$tmp1
    

    eine zusätzliche Variable, deren Name der Wert von ref ist, wird implizit deklariert. Der Wert von tmp1 Wird auch indirekt ​​der implizit deklarierten Variablen durch diese explizite Zuordnung zu ref zugewiesen.

Im Kontext von Ihre verknüpfte Frage wird read_and_verify Als aufgerufen

read_and_verify domain "Prompt text here..."

deklariert die Variable domain und weist ihr den Wert tmp1 zu (d. h. die Benutzereingabe). Es wurde genau entwickelt, um den Code, der mit dem Benutzer interagiert, wiederzuverwenden und eine nameref-Variable zu nutzen, um domain und einige andere Variablen zu deklarieren.

Um den impliziten Teil genauer zu betrachten, können wir den Prozess Schritt für Schritt reproduzieren:

## Assign a value to the first positional argument
$ set -- "domain"

## Declare the same "tmp1" variable as in your code
$ tmp1="value for domain"

## Declare a "ref" variable with the nameref attribute set and
## assign the value "domain" to it
$ declare -n ref="$1"

## Note that there is no "domain" variable yet
$ declare -p domain
bash: declare: domain: not found

## Assign a value to "ref" and, indirectly, to the "domain" variable
## that is implicitly declared  
$ ref=$tmp1

## Verify that a variable named "domain" now exists, and that
## its value is that of "tmp1"
$ declare -p domain
declare -- domain="value for domain"

## Verify that "ref" is actually a reference to "domain"
$ domain="new value"
$ echo "$domain"
new value
$ declare -p ref
declare -n ref="domain"
$ echo "$ref"
new value

1Referenz: ÄNDERT Datei, Abschnitt "3. Neue Funktionen in Bash", Punkt "w".
Dies kann relevant sein: Zum Beispiel CentOS Linux 7.6 (derzeit die neueste Version) wird mit Bash 4.2 geliefert .

2Wie bei Shell-Buildins üblich, ist eine erschöpfende nd kurze 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.

3Dieses Verhalten von declare -p Wurde in Bash 4.4 eingeführt. Referenz: ÄNDERT Datei, Abschnitt "3. Neue Funktionen in Bash", Punkt "f".
Wie G-Man in Kommentaren ausgeführt hat, ergibt Bash 4.3 declare name; declare -p name Einen Fehler. Sie können jedoch weiterhin überprüfen, ob name mit declare -p | grep 'declare -- name' Existiert.

4FullBashGuide, Parameter auf mywiki.wooledge.org

13
fra-san

Ich werde versuchen, dies zu erklären, aber verzeihen Sie mir, wenn ich dem von Ihnen angegebenen Beispiel nicht folge. Ich werde eher 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 declare auf seiner höchsten Grundebene nur eine Möglichkeit ist, Bash mitzuteilen, dass Sie eine Variable benötigen value (dh ein Wert, der sich während der Skriptausführung möglicherweise ändert) und auf den Sie mit einem bestimmten Namen verweisen, genau dem Namen, den Sie neben dem Befehl declare selbst angeben.

Das ist:

declare foo="bar"

teilt Bash mit, dass die Variable foo den Wert bar haben soll.

Aber ... Moment mal ... wir können das tun, ohne declare zu verwenden, oder? Wie in:

foo="bar"

Sehr richtig.

Nun, es kommt vor, dass die obige einfache Zuweisung tatsächlich eine implizite Möglichkeit ist, eine Variable zu deklarieren.

( Es kommt auch vor, dass das Obige eine von einigen Möglichkeiten ist, um den Wert der genannten Variablen zu ändern (---) foo; in der Tat ist es genau der direkteste, prägnanteste, offensichtlichste und direkteste Weg .. aber es ist nicht der einzige .. .. Ich werde später darauf zurückkommen .. =).

Aber wenn es so gut möglich ist, einen "Namen, der der Kürze halber Variablenwerte markiert" (der Kürze halber nur "Variable") zu deklarieren, ohne declare überhaupt zu verwenden, warum würden Sie das jemals tun? Möchten Sie diesen pompösen Befehl "deklarieren" verwenden?

Die Antwort liegt in der Tatsache, dass die obige implizite Art, eine Variable zu deklarieren (foo="bar"), implizit dazu führt, dass Bash diese Variable als Typ betrachtet, der im typischen Verwendungsszenario für am häufigsten verwendet wird höllisch.

Ein solcher Typ ist der Zeichenfolgentyp, d. H. 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, und eine implizite Deklaration wie foo=5+6 nicht verwenden Bash soll foo den Wert 11 zuweisen, wie Sie es vielleicht erwarten. Es wird eher foo die Reihenfolge der drei Zeichen 5+6 zugewiesen.

Sie müssen also Bash mitteilen, dass foo als Zahl und nicht als Zeichenfolge betrachtet werden soll. Dafür ist ein explizites 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 foo den Wert numerisch 11 zu.

Das heißt: Wenn Sie declare -i foo sagen, geben Sie der Variablen foo das Attribut als Ganzzahl.

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 von 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 declare -u foo sagen, weist Bash von nun an tatsächlich die Zeichenfolge BAR zu, wenn Sie foo=bar sagen zur Variablen foo.

Um einer Variablen eines dieser Attribute zuzuweisen, verwenden Sie muss den Befehl declare. Es gibt keine andere Wahl.


Eines der Attribute, die Sie über declare angeben können, ist das berüchtigte Attribut "name-ref", das Attribut -n. ( Und jetzt werde ich das Konzept wieder aufnehmen, das ich früher 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.

Hier ist , wie es funktioniert:

Sie declare eine Variable mit dem Attribut -n, und es wird sehr empfohlen (obwohl nicht unbedingt erforderlich, aber es macht die Dinge einfacher), dass Sie auch eine geben value to this sehr variabel im selben declare Befehl. So was:

declare -n baz="foo"

Dies teilt Bash mit, dass von nun an jedes Mal, wenn Sie den Wert der Variablen mit dem Namen baz verwenden oder ändern, der Wert der Variablen mit dem Namen foo.

Was bedeutet, dass Sie von da an so etwas wie baz=10+3 sagen können, damit foo den Wert 13 erhält. Angenommen, foo wurde zuvor als Ganzzahl deklariert ( declare -i) wie vor einer Minute, sonst wird die Reihenfolge der vier Zeichen angezeigt. 10+3.

Außerdem: Wenn Sie den Wert von foo direkt ändern, wie in foo=15, sehen Sie 15 auch, indem Sie echo “${baz}” sagen. Dies liegt daran, dass die als name-ref von baz deklarierte Variable foo immer den Wert von foo widerspiegelt.

Der obige Befehl declare -n wird als "Namensreferenz" bezeichnet, da er die Variable baz refer zum name von macht eine andere Variable. Tatsächlich haben wir deklariert, dass baz den Wert "foo" hat, der aufgrund der Option -n von Bash als Name für eine andere Variable behandelt wird.

Nun, warum auf der Erde würdest du das jemals tun wollen?

Es ist erwähnenswert, dass dies eine Funktion für fortgeschrittene Anforderungen 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 auch ist, dass ein solches Problem eher durch die Verwendung einer geeigneten Programmiersprache anstelle von Bash behoben werden sollte.

Eine dieser fortgeschrittenen Anforderungen ist beispielsweise, wenn Sie als Programmierer während der Entwicklung nicht wissen können, welche Variable welche Sie in einem bestimmten Punkt eines Skripts verwenden müssen, aber sie will zur Laufzeit dynamisch vollständig bekannt sein. Und da es für einen Programmierer keine Möglichkeit gibt, zur Laufzeit einzugreifen, besteht die einzige Möglichkeit darin, vorher für eine solche Situation im Skript vorzusehen, und ein "Name-Ref" kann der sein nur praktikabler Weg. Als weithin bekannter Anwendungsfall dieses fortgeschrittenen Bedarfs sollten Sie beispielsweise an Plug-Ins denken. 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.

Ein weiteres fortgeschrittenes Bedürfnis ist, wenn Sie mit einer großen Datenmenge umgehen müssen in RAM und Sie müssen diese Daten auch an Funktionen Ihres Skripts übergeben, die = auch müssen diese Daten auf dem Weg ändern. In einem solchen Fall könnten Sie sicherlich kopieren diese Daten von einer Funktion zur anderen (wie Bash, wenn Sie dest_var="${src_var}" ausführen oder wenn Sie Funktionen wie in myfunc "${src_var}" aufrufen), aber Da diese Daten eine große Menge sind, würde dies zu einer enormen Verschwendung von RAM und für einen sehr ineffizienten Betrieb führen. Die Lösung, wenn solche Situationen auftreten, besteht darin, nicht eine Kopie der Daten zu verwenden, sondern eine Referenz auf diese 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 ebenso üblich ist wie sehr in den richtigen Programmiersprachen veraltet.

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:

  1. variablen, die als "indizierte Arrays" deklariert sind (declare -a)
  2. variablen, die als "assoziative Arrays" deklariert sind (declare -A).

Hierbei handelt es sich um eine Art von Variablen, die einfacher (sowie effizienter) Funktionen mithilfe von Namensreferenzen anstelle von normalem Kopieren weitergeben können, selbst wenn sie keine großen Mengen enthalten von Dateien.

Wenn all diese Beispiele seltsam und immer noch unverständlich klingen, liegt dies nur daran, dass Namensreferenzen in der Tat ein fortgeschrittenes Thema sind und das typische Verwendungsszenario von Bash nur 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, dass das meiste Sie im Moment tun sollten, indem Sie einfach mit Namensreferenzen experimentieren, indem Sie die einfachen Beispiele verwenden, die ich gezeigt habe, und möglicherweise mit dem von Ihnen bereitgestellten Beispielcode, wobei Sie für den Moment das „ Warum um alles in der Welt “und konzentriert sich nur auf den 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.

7
LL3

Im Allgemeinen setzt declare in der Shell bash (oder entfernt oder zeigt) Attribute für Variablen. 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 eingebaute typeset ist ein Synonym für declare in bash, da typeset in anderen Shells (ksh, wo es verwendet wird, verwendet wird entstanden und zsh (zum Beispiel) zum Festlegen von Variablenattributen.


Schauen Sie sich das Namensreferenzbeispiel in der Frage genauer an:

Die Shell-Funktion, die Sie anzeigen, mit einem zusätzlichen Code, der sie verwendet:

#!/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
}

read_and_verify foo
printf 'The variable "foo" has the value "%s"\n' "$foo"

Führen Sie dies aus:

bash script.sh
 Bitte geben Sie den Wert für 'foo' ein: hallo
 Bitte wiederholen Sie den Wert, um Folgendes zu überprüfen: hallo?
 Werte unübertroffen. Bitte versuchen Sie es erneut. 
 Die Variable "foo" hat den Wert "" 

Dies zeigt, dass die Variable foo auf nichts gesetzt wird, wenn der Benutzer zwei verschiedene Zeichenfolgen eingibt.

bash script.sh
 Bitte geben Sie den Wert für 'foo' ein: hallo
 Bitte wiederholen Sie den Wert, um Folgendes zu überprüfen: hallo
 Die Variable "foo" hat den Wert "hallo" 

Dies zeigt, dass die Variable foo auf die Zeichenfolge gesetzt wird, die der Benutzer eingegeben hat, als er die Zeichenfolge same zweimal eingegeben hat.

Die Art und Weise, wie $foo Im Hauptteil des Skripts den Wert hello erhält, wird in der Shell-Funktion in den folgenden Zeilen beschrieben:

declare -n ref="$1"
ref=$tmp1

dabei ist $tmp1 die vom Benutzer eingegebene Zeichenfolge hello und $1 die Zeichenfolge foo, die in der Befehlszeile der Funktion vom Hauptteil der übergeben wird Skript.

Beachten Sie, dass die Variable ref mit declare -n Als Namensreferenzvariable deklariert wird und dass der Wert foo 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 Variablen ref mit der Verwendung von foo identisch ist. Die Variable ref ist eine Namensreferenzvariable, die an dieser Stelle auf foo verweist.

Dies hat zur Folge, dass durch Zuweisen eines Werts zu ref, wie in der Zeile nach der Deklaration, der Wert foo zugewiesen wird.

Der Wert hello ist dann in $foo Im Hauptteil des Skripts verfügbar.

6
Kusalananda