Verarbeitung eines Kommandos, eval

Jedes Kommando wird von der Shell einer command expansion unterzogen, bevor es gestartet wird. Dazu wird nacheinander ausgeführt:

Für Details (insbesondere, wann und wie Sonderzeichen berücksichtigt oder ignoriert werden): siehe info bash.



Manchmal ist es nötig, die Auswertung eines Kommandos mehrfach durchführen zu lassen. Hat man beispielsweise in einer Variablen i eine Zahl, und will anhand dieser Variablen auf den soundsovielten Positionsparameter zugreifen, hat man ein Problem. Angenommen, i hat den Wert zwei, und man will dann den Wert von $2 verwenden. Dann liegt es nahe, einen Ausdruck der Art ${$i} zu verwenden. Das klappt nur leider nicht, weil die Variablenersetzung nur ,,einstufig`` durchgeführt wird: $i wird zwar durch eine 2 ersetzt, aber der daraus resultierende Teilausdruck ${2} erzeugt eine Fehlermeldung, weil zu diesem Zeitpunkt keine weitere Parameterersetzung mehr durchgeführt wird (die Variablenersetzung fand ja bereits statt).

Eine Lösung dafür hat man mit dem internen Kommando eval.

eval wird mit einem nachfolgenden Kommando aufgerufen, und läßt das ihm übergebene Kommando ein zweites Mal durch die obige command expansion laufen, führt dann das Kommando aus, und liefert selbst den Rückgabewert des Kommandos zurück.

Diesen Mechanismus kann man auch mehrfach anwenden: Wenn ein Kommando einmal die Ersetzung durchläuft, dann führt eval Kommando zu einer doppelten Ersetzung, und eval eval Kommando zu einer dreifachen.

Beispiel 8   Ausgeben des zweiten Positionsparameters anhand des Werts 2 der Variablen i:
klaus@aw35:~ > set wer würmer hat ist nie allein
klaus@aw35:~ > i=2
klaus@aw35:~ > echo \$$i
$2
klaus@aw35:~ > echo $$i
783i
klaus@aw35:~ > echo ${$i}
bash: ${$i}: bad substitution
klaus@aw35:~ > eval echo \${$i}
würmer
Der erste Versuch (\$$i) bringt nicht das erhoffte Ergebnis: \$ wird zwar zu einem einzelnen Dollarzeichen, und das folgende $i zu einer 2 expandiert. Es fehlt aber daran, das resultierende $2 nochmals auszuwerten. Stattdessen wird $2 ausgegeben.

Im zweiten Versuch hat man mit $$i auch nicht mehr Glück: $$ wird durch die aktuelle process id der Shell ersetzt (hier: 783), und mit einem nachfolgenden i ausgegeben.

Der dritte Versuch (${$i}) bringt einen auch nicht weiter: das resultierende ${2} ist nach der Ersetzung von Parametern ein ungültiger Ausdruck.

Freude kommt erst beim letzten Versuch auf: durch das eval wird das ganze Kommando zweimal durch die command expansion geschickt, bevor es ausgeführt wird. Beim ersten Durchlauf läßt man $i zu einer 2 expandieren, und beim zweiten Durchlauf mit einem vorangestellten $ zum Wert des zweiten Positionsparameters (würmer). Damit dieses vorangestellte Dollarzeichen aber überhaupt unbeschadet die erste Ersetzung übersteht, muß man einen back slash (\) voranstellen. Beim ersten Ersetzen wird also \$ zu einem Dollarzeichen, und $i zu einer 2. Beim zweiten Ersetzen entsteht aus dem dann vorliegenden ${2} tatsächlich der Wert der Variablen, nämlich würmer.

Die Angabe der geschweiften Klammern ist hier übrigens nicht nötig; das Kommando eval echo \$$i ist gleichwertig, und funktioniert im Gegensatz zu der Lösung mit Klammern nicht nur in der bash, sondern auch mit der Bourne-Shell (/bin/sh). Ich finde aber die Version mit Klammern verständlicher3.6.

AnyWare@Wachtler.de