Wie führe ich einen in einer Variablen gespeicherten Befehl aus?

82

Was ist der richtige Weg, um einen in einer Variable gespeicherten Befehl aufzurufen?
Gibt es Unterschiede zwischen 1 und 2?

#!/bin/sh
cmd="ls -la $APPROOTDIR | grep exception"
#1
$cmd
#2
eval "$cmd"
Volodymyr Bezuglyy
quelle
6
Bitte siehe BashFAQ / 050 .
Bis auf weiteres angehalten.

Antworten:

94

Unix-Shells führen eine Reihe von Transformationen in jeder Eingabezeile durch, bevor sie ausgeführt werden. Für die meisten Shells sieht es ungefähr so ​​aus (aus der bashManpage entnommen ):

  • anfängliche Wortaufteilung
  • Klammererweiterung
  • Tilde-Erweiterung
  • Parameter-, Variablen- und arithmetische Erweiterung
  • Befehlsersetzung
  • sekundäre Wortaufteilung
  • Pfaderweiterung (auch bekannt als Globbing)
  • Entfernung von Zitaten

Durch die $cmddirekte Verwendung wird es während der Parametererweiterungsphase durch Ihren Befehl ersetzt und anschließend allen folgenden Transformationen unterzogen.

Mit using eval "$cmd"wird nichts unternommen, bis die Phase zum Entfernen des Anführungszeichens unverändert $cmdzurückgegeben und als Parameter an übergeben wird eval, dessen Funktion darin besteht, die gesamte Kette vor der Ausführung erneut auszuführen.

Im Grunde sind sie in den meisten Fällen gleich und unterscheiden sich, wenn Ihr Befehl die Transformationsschritte bis zur Parametererweiterung verwendet. Beispiel: Verwenden der Klammererweiterung:

$ cmd="echo foo{bar,baz}"
$ $cmd
foo{bar,baz}
$ eval "$cmd"
foobar foobaz
JB.
quelle
6
Wie geht man eval "$cmd"ohne zu schreiben eval? $($cmd)? ${$cmd}?
Steven Lu
2
@StevenLu, keines davon ist äquivalent - absichtlich: Eine evalOperation analysiert Daten als Syntax; Es ist daher sehr sicherheitsrelevant, und es implizit zu tun, wäre eine sehr schlechte Form.
Charles Duffy
5

Wenn Sie nur tun , eval $cmd wenn wir das tun cmd="ls -l"(interaktiv und in einem Skript) wir das gewünschte Ergebnis erhalten. In Ihrem Fall haben Sie eine Pipe mit einem Grep ohne Muster, sodass der Grep-Teil mit einer Fehlermeldung fehlschlägt. Nur $cmdwird eine "Befehl nicht gefunden" (oder eine solche) Nachricht generiert. Versuchen Sie also, eval zu verwenden, und verwenden Sie einen fertigen Befehl, der keine Fehlermeldung generiert.

Henno Brandsma
quelle
Es war nur ein Druckfehler. Sollte "grep Ausnahme" sein.
Volodymyr Bezuglyy
Verwenden Sie dann eval, nicht $ cmd für sich, da es wahrscheinlich nicht funktionieren wird (bei meinen Tests unter bash und zsh nicht). Dies ist, was eval tun sollte ...
Henno Brandsma
0

$cmdwürde nur die Variable durch ihren Wert ersetzen, der in der Befehlszeile ausgeführt werden soll. eval "$cmd"Führt eine Variablenerweiterung und Befehlssubstitution durch, bevor der resultierende Wert in der Befehlszeile ausgeführt wird

Die zweite Methode ist hilfreich, wenn Sie Befehle ausführen möchten, die nicht flexibel sind, z.
for i in {$a..$b}
Die Formatschleife funktioniert nicht, da sie keine Variablen zulässt.
In diesem Fall ist eine Pipe zum Bash oder Eval eine Problemumgehung.

Getestet unter Mac OSX 10.6.8, Bash 3.2.48

Zimba
quelle
-4

Ich denke du solltest sagen

`

(Backtick) Symbole um Ihre Variable.

Nickolodeon
quelle
4
Dies führt die Ausgabe des Befehls aus, der z. B. im Fall von ls -l eine Nachricht wie "total" Befehl nicht gefunden "erzeugt (weil total ... Teil der Ausgabe von ls -l ist, z. B.) NICHT was Sie wollen.
Henno Brandsma