So verwenden Sie eval in Linux-Bash-Skripten
Veröffentlicht: 2022-08-22 Von allen Bash-Befehlen hat der arme alte eval
wahrscheinlich den schlechtesten Ruf. Berechtigt oder nur schlechte Presse? Wir besprechen die Verwendung und die Gefahren dieses am wenigsten geliebten Linux-Befehls.
Wir müssen über eval sprechen
Unachtsam eingesetzt, kann eval
zu unvorhersehbarem Verhalten und sogar zu Systemunsicherheiten führen. So wie es klingt, sollten wir es wahrscheinlich nicht verwenden, oder? Nicht ganz.
Ähnliches könnte man über Autos sagen. In den falschen Händen sind sie eine tödliche Waffe. Die Leute benutzen sie bei Rammangriffen und als Fluchtfahrzeuge. Sollten wir alle auf das Auto verzichten? Nein natürlich nicht. Aber sie müssen richtig benutzt werden und von Leuten, die wissen, wie man sie fährt.
Das übliche Adjektiv für eval
ist „böse“. Aber es hängt alles davon ab, wie es verwendet wird. Der Befehl eval
sammelt die Werte einer oder mehrerer Variablen. Es erstellt eine Befehlszeichenfolge. Es führt dann diesen Befehl aus. Dies macht es nützlich, wenn Sie mit Situationen fertig werden müssen, in denen der Inhalt eines Befehls während der Ausführung Ihres Skripts dynamisch abgeleitet wird.
Probleme treten auf, wenn ein Skript geschrieben wird, um eval
auf eine Zeichenfolge anzuwenden, die von irgendwo außerhalb des Skripts empfangen wurde. Es kann von einem Benutzer eingegeben, über eine API gesendet, an eine HTTPS-Anforderung getaggt oder an einer anderen Stelle außerhalb des Skripts verwendet werden.
Wenn die Zeichenfolge, an der eval
arbeiten wird, nicht lokal und programmgesteuert abgeleitet wurde, besteht die Gefahr, dass die Zeichenfolge eingebettete böswillige Anweisungen oder andere schlecht formatierte Eingaben enthält. Offensichtlich möchten Sie nicht, dass eval
böswillige Befehle ausführt. Verwenden Sie eval
also sicherheitshalber nicht mit extern generierten Zeichenfolgen oder Benutzereingaben.
Erste Schritte mit eval
Der eval
-Befehl ist ein integrierter Bash-Shell-Befehl. Wenn Bash vorhanden ist, wird eval
vorhanden sein.
eval
verkettet seine Parameter zu einem einzigen String. Es wird ein einzelnes Leerzeichen verwendet, um verkettete Elemente zu trennen. Es wertet die Argumente aus und übergibt dann die gesamte Zeichenfolge zur Ausführung an die Shell.
Lassen Sie uns eine Variable namens wordcount
.
wordcount="wc -w raw-notes.md"
Die String-Variable enthält einen Befehl zum Zählen der Wörter in einer Datei namens „raw-notes.md“.
Wir können eval
verwenden, um diesen Befehl auszuführen, indem wir ihm den Wert der Variablen übergeben.
eval " $wordcount "
Der Befehl wird in der aktuellen Shell ausgeführt, nicht in einer Subshell. Das können wir leicht zeigen. Wir haben eine kurze Textdatei namens „variables.txt“. Es enthält diese beiden Zeilen.
first=Anleitung zweite = Aussenseiter
Wir verwenden cat
, um diese Zeilen an das Terminalfenster zu senden. Dann verwenden wir eval
, um einen cat
-Befehl auszuwerten, damit die Anweisungen in der Textdatei ausgeführt werden. Dadurch werden die Variablen für uns festgelegt.
cat-Variablen.txt eval "$(cat variables.txt)" echo $erster $zweiter
Durch die Verwendung von echo
zum Drucken der Werte der Variablen können wir sehen, dass der Befehl eval
in der aktuellen Shell ausgeführt wird, nicht in einer Subshell.
Ein Prozess in einer Subshell kann die Shell-Umgebung des übergeordneten Prozesses nicht ändern. Da eval in der aktuellen Shell ausgeführt wird, können die von eval
gesetzten Variablen von der Shell verwendet werden, die den eval
-Befehl gestartet hat.
Beachten Sie, dass bei Verwendung von eval
in einem Skript die Shell, die von eval
geändert würde, die Subshell ist, in der das Skript ausgeführt wird, und nicht die Shell, die es gestartet hat.
VERWANDT: So verwenden Sie die Linux-Befehle cat und tac
Verwenden von Variablen in der Befehlszeichenfolge
Wir können andere Variablen in die Befehlszeichenfolgen aufnehmen. Wir werden zwei Variablen so einstellen, dass sie ganze Zahlen enthalten.
num1=10 num2=7
Wir erstellen eine Variable für einen expr
-Befehl, der die Summe zweier Zahlen zurückgibt. Das bedeutet, dass wir im Befehl auf die Werte der beiden Integer-Variablen zugreifen müssen. Beachten Sie die Backticks um die expr
Anweisung.
add="`Ausdruck $num1 + $num2`"
Wir erstellen einen weiteren Befehl, um uns das Ergebnis der expr
Anweisung anzuzeigen.
show="echo"
Beachten Sie, dass wir weder am Ende des echo
-Strings noch am Anfang des expr
-Strings ein Leerzeichen einfügen müssen. eval
kümmert sich darum.
Und um den gesamten Befehl auszuführen, verwenden wir:
eval $show $add
Die Variablenwerte innerhalb des expr
-Strings werden durch eval
in den String eingesetzt, bevor er zur Ausführung an die Shell übergeben wird.
VERWANDT: So arbeiten Sie mit Variablen in Bash
Zugriff auf Variablen innerhalb von Variablen
Sie können einer Variablen einen Wert zuweisen und dann den Namen dieser Variablen einer anderen Variablen zuweisen. Mit eval
können Sie auf den Wert zugreifen, der in der ersten Variablen enthalten ist, und zwar über ihren Namen, der der in der zweiten Variablen gespeicherte Wert ist. Ein Beispiel wird Ihnen helfen, das zu entwirren.
Kopieren Sie dieses Skript in einen Editor und speichern Sie es als Datei namens „assign.sh“.
#!/bin/bash title="How-to-Geek" Webseite=Titel Befehl = "Echo" eval $command \${$webpage}
Wir müssen es mit dem Befehl chmod
ausführbar machen.
chmod +x assign.sh
Sie müssen dies für alle Skripts tun, die Sie aus diesem Artikel kopieren. Verwenden Sie einfach jeweils den passenden Skriptnamen.
Wenn wir unser Skript ausführen, sehen wir den Text aus der Variable title
, obwohl der Befehl eval
die Variable webpage
verwendet.
./assign.sh
Das maskierte Dollarzeichen „ $
“ und die geschweiften Klammern „ {}
“ bewirken, dass eval den Wert in der Variablen betrachtet, deren Name in der webpage
gespeichert ist.
Dynamisch erstellte Variablen verwenden
Wir können eval
verwenden, um Variablen dynamisch zu erstellen. Dieses Skript heißt „loop.sh“.
#!/bin/bash Gesamt = 0 label="Schleife abgeschlossen. Gesamt:" für n in {1..10} tun eval x$n=$n Echo "Schleife" $x$n ((gesamt+=$x$n)) erledigt echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10 echo $label $total
Es erstellt eine Variable namens total
, die die Summe der Werte der von uns erstellten Variablen enthält. Anschließend erstellt es eine Zeichenfolgenvariable namens label
. Dies ist eine einfache Textfolge.
Wir werden 10 Schleifen durchlaufen und 10 Variablen mit den Namen x1
bis x10
. Die eval
Anweisung im Hauptteil der Schleife liefert das „x“ und nimmt den Wert des Schleifenzählers $n
, um den Variablennamen zu erstellen. Gleichzeitig setzt er die neue Variable auf den Wert des Schleifenzählers $n
.
Es gibt die neue Variable im Terminalfenster aus und erhöht dann die total
um den Wert der neuen Variablen.
Außerhalb der Schleife werden die 10 neuen Variablen noch einmal gedruckt, alle in einer Zeile. Beachten Sie, dass wir auf die Variablen auch mit ihren echten Namen verweisen können, ohne eine berechnete oder abgeleitete Version ihrer Namen zu verwenden.
Abschließend geben wir den Wert der total
aus.
./loop.sh
RELATED: Primer: Bash Schleifen: for, while und until
Verwenden von eval mit Arrays
Stellen Sie sich ein Szenario vor, in dem Sie ein Skript haben, das lange ausgeführt wird und einige Verarbeitungsschritte für Sie durchführt. Es schreibt in eine Protokolldatei mit einem aus einem Zeitstempel erstellten Namen. Gelegentlich wird eine neue Protokolldatei erstellt. Wenn das Skript beendet ist und keine Fehler aufgetreten sind, löscht es die von ihm erstellten Protokolldateien.
Sie möchten nicht, dass es einfach rm *.log
, sondern nur die erstellten Protokolldateien löscht. Dieses Skript simuliert diese Funktionalität. Dies ist „clear-logs.sh“.
#!/bin/bash deklarieren Sie -a Protokolldateien Dateianzahl=0 rm_string="echo" Funktion create_logfile() { ((++Dateianzahl)) filename=$(date +"%Y-%m-%d_%H-%M-%S").log logfiles[$filecount]=$Dateiname echo $filecount "Erstellt" ${logfiles[$filecount]} } # Hauptteil des Skripts. Hier wird eine gewisse Verarbeitung durchgeführt # generiert regelmäßig eine Protokolldatei. Das simulieren wir create_logfile schlafen 3 create_logfile schlafen 3 create_logfile schlafen 3 create_logfile # Gibt es Dateien, die entfernt werden müssen? for ((file=1; file<=$filecount; file++)) tun # Entfernen Sie die Protokolldatei eval $rm_string ${logfiles[$file]} "gelöscht..." logfiles[$file]="" erledigt
Das Skript deklariert ein Array namens logfiles
. Diese enthält die Namen der Protokolldateien, die vom Skript erstellt werden. Es deklariert eine Variable namens filecount
. Diese enthält die Anzahl der erstellten Protokolldateien.
Es deklariert auch einen String namens rm_string
. In einem realen Skript würde dies den Befehl rm
enthalten, aber wir verwenden echo
, damit wir das Prinzip auf zerstörungsfreie Weise demonstrieren können.
Die Funktion create_logfile()
ist, wo jede Protokolldatei benannt wird und wo sie geöffnet werden würde. Wir erstellen nur den Dateinamen und geben vor, dass er im Dateisystem erstellt wurde.
Die Funktion erhöht die filecount
Variable. Sein Anfangswert ist Null, also wird der erste Dateiname, den wir erstellen, an Position eins im Array gespeichert. Dies geschieht absichtlich, siehe auch später.
Der Dateiname wird mit dem date
und der Erweiterung „.log“ erstellt. Der Name wird im Array an der durch filecount
angegebenen Position gespeichert. Der Name wird im Terminalfenster ausgegeben. In einem realen Skript würden Sie auch die eigentliche Datei erstellen.
Der Hauptteil des Skripts wird mit dem Befehl sleep
simuliert. Es erstellt die erste Protokolldatei, wartet drei Sekunden und erstellt dann eine weitere. Es erstellt vier Protokolldateien, die so verteilt sind, dass die Zeitstempel in ihren Dateinamen unterschiedlich sind.
Schließlich gibt es eine Schleife, die die Protokolldateien löscht. Die Schleifenzählerdatei wird auf eins gesetzt. Es zählt bis einschließlich dem Wert von filecount
, der die Anzahl der erstellten Dateien enthält.
Wenn filecount
immer noch auf null gesetzt ist – weil keine Protokolldateien erstellt wurden –, wird der Schleifenkörper nie ausgeführt, da eins nicht kleiner oder gleich null ist. Aus diesem Grund wurde die filecount
-Variable bei ihrer Deklaration auf Null gesetzt und vor der Erstellung der ersten Datei inkrementiert.
Innerhalb der Schleife verwenden wir eval
mit unserem nicht-destruktiven rm_string
und dem Namen der Datei, die aus dem Array abgerufen wird. Dann setzen wir das Array-Element auf einen leeren String.
Das sehen wir, wenn wir das Skript ausführen.
./clear-logs.sh
Es ist nicht alles schlecht
Das viel gescholtene eval
hat definitiv seinen Nutzen. Wie die meisten Werkzeuge ist es gefährlich, rücksichtslos eingesetzt zu werden, und zwar in mehr als einer Hinsicht.
Wenn Sie sicherstellen, dass die Zeichenfolgen, an denen es arbeitet, intern erstellt und nicht von Menschen, APIs oder Dingen wie HTTPS-Anfragen erfasst werden, vermeiden Sie die größten Fallstricke.
VERWANDT: So zeigen Sie Datum und Uhrzeit im Linux-Terminal an (und verwenden es in Bash-Skripten)