Ich bin verwirrt über ein Bash-Skript.
Ich habe folgenden Code:
function grep_search() {
magic_way_to_define_magic_variable_$1=`ls | tail -1`
echo $magic_variable_$1
}
Ich möchte in der Lage sein, einen Variablennamen zu erstellen, der das erste Argument des Befehls enthält und den Wert von z. B. der letzten Zeile von trägt ls
.
Um zu veranschaulichen, was ich will:
$ ls | tail -1
stack-overflow.txt
$ grep_search() open_box
stack-overflow.txt
Wie soll ich also definieren / deklarieren $magic_way_to_define_magic_variable_$1
und wie soll ich es im Skript aufrufen?
Ich habe versucht eval
, ${...}
, \$${...}
, aber ich bin immer noch verwirrt.
Antworten:
Verwenden Sie ein assoziatives Array mit Befehlsnamen als Schlüssel.
Wenn Sie keine assoziativen Arrays verwenden können (z. B. müssen Sie
bash
3 unterstützen), können Siedeclare
dynamische Variablennamen erstellen:und verwenden Sie die indirekte Parametererweiterung, um auf den Wert zuzugreifen.
Siehe BashFAQ: Indirektion - Auswertung indirekter / Referenzvariablen .
quelle
-a
deklariert ein indiziertes Array, kein assoziatives Array. Sofern das Argument togrep_search
keine Zahl ist, wird es als Parameter mit einem numerischen Wert behandelt (der Standardwert ist 0, wenn der Parameter nicht festgelegt ist).4.2.45(2)
und deklariere, dass es nicht als Option aufgeführt istdeclare: usage: declare [-afFirtx] [-p] [name[=value] ...]
. Es scheint jedoch richtig zu funktionieren.declare -h
in 4.2.45 (2) für mich zeigtdeclare: usage: declare [-aAfFgilrtux] [-p] [name[=value] ...]
. Sie können überprüfen, ob Sie tatsächlich 4.x und nicht 3.2 ausführen.declare $varname="foo"
?${!varname}
ist viel einfacher und weitgehend kompatibelIch habe in letzter Zeit nach einer besseren Möglichkeit gesucht. Assoziatives Array klang für mich wie ein Overkill. Schau mal was ich gefunden habe:
...und dann...
quelle
prefix_${middle}_postfix
(dh Ihre Formatierung würde nicht funktionierenvarname=$prefix_suffix
)Neben assoziativen Arrays gibt es in Bash verschiedene Möglichkeiten, dynamische Variablen zu erzielen. Beachten Sie, dass alle diese Techniken Risiken bergen, die am Ende dieser Antwort erläutert werden.
In den folgenden Beispielen gehe ich davon aus, dass
i=37
und dass Sie die benannte Variable mit demvar_37
Anfangswert aliasisieren möchtenlolilol
.Methode 1. Verwenden einer Zeigervariablen
Sie können den Namen der Variablen einfach in einer Indirektionsvariablen speichern, ähnlich wie bei einem C-Zeiger. Bash hat dann eine Syntax zum Lesen der Alias-Variablen: Erweitert
${!name}
sich auf den Wert der Variablen, deren Name der Wert der Variablen istname
. Sie können sich das als zweistufige Erweiterung${!name}
vorstellen : erweitert sich zu$var_37
, erweitert sich zulolilol
.Leider gibt es keine Gegenstücksyntax für Ändern der Alias-Variablen. Stattdessen können Sie die Zuordnung mit einem der folgenden Tricks erreichen.
1a. Zuweisen mit
eval
eval
ist böse, aber auch der einfachste und tragbarste Weg, um unser Ziel zu erreichen. Sie müssen die rechte Seite der Aufgabe vorsichtig verlassen, da sie zweimal ausgewertet wird . Eine einfache und systematische Möglichkeit besteht darin, die rechte Seite vorher zu bewerten (oder zu verwendenprintf %q
).Und Sie sollten manuell überprüfen, ob die linke Seite ein gültiger Variablenname oder ein Name mit Index ist (was wäre, wenn dies der Fall wäre
evil_code #
?). Im Gegensatz dazu erzwingen alle anderen unten aufgeführten Methoden dies automatisch.Nachteile:
eval
ist böseeval
ist böseeval
ist böse1b. Zuweisen mit
read
Mit dem integrierten
read
Programm können Sie einer Variablen, deren Namen Sie geben, Werte zuweisen. Diese Tatsache kann in Verbindung mit Here-Strings ausgenutzt werden:Das
IFS
Teil und die Option-r
stellen sicher, dass der Wert unverändert zugewiesen wird, während die Option-d ''
das Zuweisen mehrzeiliger Werte ermöglicht. Aufgrund dieser letzten Option wird der Befehl mit einem Exit-Code ungleich Null zurückgegeben.Beachten Sie, dass, da wir eine Here-Zeichenfolge verwenden, ein Zeilenumbruchzeichen an den Wert angehängt wird.
Nachteile:
1c. Zuweisen mit
printf
Seit Bash 3.1 (veröffentlicht 2005) kann das
printf
eingebaute Ergebnis auch einer Variablen zugewiesen werden, deren Name angegeben ist. Im Gegensatz zu den vorherigen Lösungen funktioniert es einfach, es ist kein zusätzlicher Aufwand erforderlich, um den Dingen zu entkommen, eine Aufteilung zu verhindern und so weiter.Nachteile:
Methode 2. Verwenden einer Referenzvariablen
Seit Bash 4.3 (veröffentlicht 2014) verfügt das integrierte
declare
Programm über eine Option-n
zum Erstellen einer Variablen, die eine „Namensreferenz“ zu einer anderen Variablen darstellt, ähnlich wie C ++ - Referenzen. Genau wie in Methode 1 speichert die Referenz den Namen der Alias-Variablen, aber jedes Mal, wenn auf die Referenz zugegriffen wird (entweder zum Lesen oder zum Zuweisen), löst Bash die Indirektion automatisch auf.Darüber hinaus verfügt Bash über eine spezielle und sehr verwirrende Syntax, um den Wert der Referenz selbst zu ermitteln
${!ref}
.Dies vermeidet nicht die unten erläuterten Fallstricke, macht aber zumindest die Syntax einfach.
Nachteile:
Risiken
Alle diese Aliasing-Techniken bergen verschiedene Risiken. Der erste führt jedes Mal beliebigen Code aus, wenn Sie die Indirektion auflösen (entweder zum Lesen oder zum Zuweisen) . Anstelle eines skalaren Variablennamens wie
var_37
können Sie auch einen Array-Index wie verwendenarr[42]
. Bash wertet den Inhalt der eckigen Klammern jedoch jedes Mal aus, wenn er benötigt wird, sodass Aliasingarr[$(do_evil)]
unerwartete Auswirkungen hat. Verwenden Sie diese Techniken daher nur, wenn Sie die Herkunft des Alias steuern .Das zweite Risiko besteht darin, einen zyklischen Alias zu erstellen. Da Bash-Variablen durch ihren Namen und nicht durch ihren Bereich identifiziert werden, können Sie versehentlich einen Alias für sich selbst erstellen (während Sie denken, dass dies eine Variable aus einem umschließenden Bereich alias wäre). Dies kann insbesondere bei Verwendung allgemeiner Variablennamen (wie
var
) geschehen . Als Folge verwendet nur diese Techniken , wenn Sie den Namen des Alias - Variable steuern .Quelle:
quelle
${!varname}
Technik eine Zwischenvariable für erfordertvarname
.Das folgende Beispiel gibt den Wert von $ name_of_var zurück
quelle
echo
s mit einer Befehlsersetzung (bei der Anführungszeichen fehlen) ist nicht erforderlich. Außerdem sollte die Option-n
gegeben werdenecho
. Und ist wie immereval
unsicher. All dies ist jedoch unnötig, da Bash zu diesem Zweck eine sicherere, klarere und kürzere Syntax hat :${!var}
.Das sollte funktionieren:
quelle
Das wird auch funktionieren
In deinem Fall
quelle
Wie pro BashFAQ / 006 , können Sie
read
mit hier String - Syntax für die Zuordnung von indirekten Variablen:Verwendung:
quelle
Verwenden
declare
Es ist nicht erforderlich, Präfixe wie bei anderen Antworten oder Arrays zu verwenden. Verwenden Sie nur
declare
, doppelte Anführungszeichen und Parameter Expansion .Ich verwende oft den folgenden Trick, um Argumentlisten zu analysieren, die Argumente enthalten , die wie folgt
one to n
formatiertkey=value otherkey=othervalue etc=etc
sind:Aber die argv liste erweitern wie
Zusätzliche Tipps
quelle
printf
odereval
Wow, der größte Teil der Syntax ist schrecklich! Hier ist eine Lösung mit einer einfacheren Syntax, wenn Sie indirekt auf Arrays verweisen müssen:
Für einfachere Anwendungsfälle empfehle ich die im Advanced Bash-Scripting Guide beschriebene Syntax .
quelle
foo_1
undfoo_2
frei von Leerzeichen und speziellen Symbolen sind. Beispiele für problematische Einträge:'a b'
Erstellt zwei Einträge im Innerenmine
.''
erstellt keinen Eintrag im Innerenmine
.'*'
wird auf den Inhalt des Arbeitsverzeichnisses erweitert. Sie können diese Probleme verhindern, indem Sie zitieren:eval 'mine=( "${foo_'"$i"'[@]}" )'
[@]
Konstrukte."${array[@]}"
wird immer auf die richtige Liste von Einträgen erweitert, ohne Probleme wie Wortaufteilung oder Erweiterung von*
. Außerdem kann das Problem der Wortaufteilung nur umgangen werden,IFS
wenn Sie ein Nicht-Null-Zeichen kennen, das niemals im Array erscheint. Darüber hinaus kann eine wörtliche Behandlung von*
nicht durch Setzen erreicht werdenIFS
. Entweder setzenIFS='*'
und teilen Sie die Sterne oder Sie setzenIFS=somethingOther
und die*
expandieren.Für indizierte Arrays können Sie sie wie folgt referenzieren:
Assoziative Arrays können in ähnlicher Weise angesprochen werden , sondern müssen den
-A
Schalter aufdeclare
statt-a
.quelle
Eine zusätzliche Methode, die nicht von der Shell- / Bash-Version abhängt, ist die Verwendung
envsubst
. Beispielsweise:quelle
script.sh
Datei:Prüfung:
Wie folgt
help eval
:Sie können
${!var}
, wie bereits erwähnt, auch die indirekte Bash- Erweiterung verwenden, sie unterstützt jedoch nicht das Abrufen von Array-Indizes.Weitere Informationen oder Beispiele finden Sie in BashFAQ / 006 zur Indirektion .
Sie sollten jedoch die Verwendung der Indirektion gemäß den folgenden Hinweisen erneut in Betracht ziehen.
quelle
varname=$prefix_suffix
Verwenden Sie für das Format einfach:quelle