So verwenden Sie eval in Linux-Bash-Skripten

Veröffentlicht: 2022-08-22
Linux-Laptop mit einer Bash-Eingabeaufforderung
fatmawati achmad zaenuri/Shutterstock.com

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.

So arbeiten Sie mit Variablen in Bash
RELATED So arbeiten Sie mit Variablen in Bash

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 "

Verwendung von eval mit einer String-Variablen zum Zählen der Wörter in einer Datei

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 

Zugriff auf Variablen, die von eval in der aktuellen Shell gesetzt wurden

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 

Verwenden von Variablen in der Befehlszeichenfolge

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 

Verwenden von chmod, um ein Skript ausführbar zu machen

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 

Zugriff auf den Wert einer Variablen über ihren Namen, der in einer anderen Variablen gespeichert ist

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 .

9 Beispiele für for-Schleifen in Linux-Bash-Skripten
RELATED 9 Beispiele für for-Schleifen in Linux-Bash-Skripten

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 

Verwenden von eval zum dynamischen Erstellen von Variablen

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.

So pausieren Sie ein Bash-Skript mit dem Linux-Sleep-Befehl
RELATED So pausieren Sie ein Bash-Skript mit dem Linux-Sleep-Befehl

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 

Löschen von Dateien, deren Namen in einem Array gespeichert sind

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)