2012-10-08 11 views
5

Jeder sagt Eval ist böse, und Sie sollten $() als Ersatz verwenden. Aber ich bin in eine Situation geraten, in der die nicht quitting nicht innerhalb $() behandelt wird.

Hintergrund ist, dass ich zu oft von Dateipfaden mit Leerzeichen in ihnen verbrannt worden bin, und so zitieren alle solche Pfade. Mehr Paranoia darüber, dass ich wissen möchte, woher alle meine ausführbaren Dateien kommen. Noch paranoider, ich vertraue mir selbst nicht und bin so in der Lage, die erstellten Befehle anzuzeigen, die ich gerade ausführen werde.

Im Folgenden werde ich versuchen Variationen vs. $ eval(), und ob der Befehl Namen zitiert wird (Cuz es könnte Leerzeichen enthalten)

BIN_LS="/bin/ls" 
    thefile="arf" 
    thecmd="\"${BIN_LS}\" -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '"/bin/ls" -ld -- "arf"' 
     ./foo.sh: line 8: "/bin/ls": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '"/bin/ls" -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    thecmd="${BIN_LS} -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access "arf": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    $("/bin/ls" -ld -- "${thefile}") 

     /bin/ls: cannot access arf: No such file or directory 

So ... das ist verwirrend. Ein in Anführungszeichen gesetzter Befehlspfad ist überall gültig, außer in einem Konstrukt $()? Ein kürzeres, direkteres Beispiel:

$ c="\"/bin/ls\" arf" 
$ $($c) 
-bash: "/bin/ls": No such file or directory 
$ eval $c 
/bin/ls: cannot access arf: No such file or directory 
$ $("/bin/ls" arf) 
/bin/ls: cannot access arf: No such file or directory 
$ "/bin/ls" arf 
/bin/ls: cannot access arf: No such file or directory 

Wie kann man den einfachen $($c) Fall erklären?

Antwort

3

Mit der Tatsache, dass es überhaupt keinen Sinn macht.

Use an array instead.
$ c=("/bin/ls" arf) 
$ "${c[@]}" 
/bin/ls: cannot access arf: No such file or directory 
+0

Unquoted Strings werden sofort ausgewertet. c = ("/ bin/ls"/bin/l *) macht etwas ganz anderes als gewünscht. (schau mit echo "'$ {c [@]}'") c = ("/ bin/ls" "/ bin/l *") dann scheitert z = $ ("$ {c [@]}"). Ein Array von Argumenten kann nicht gut mit $() verwendet werden? – Shenme

+1

Allerdings scheint c = ("/ bin/ls" "/ bin/l *") dann z = $ ($ {c [@]}) gut zu funktionieren. Ist das die ausführbare Form $(), die du mir zeigen wolltest? – Shenme

5

Die Verwendung von " Worte zu zitieren, ist Teil Ihrer Interaktion mit Bash. Bei der Eingabe von

$ "/bin/ls" arf 

an der Eingabeaufforderung oder in einem Skript, sind Sie Bash zu sagen, dass der Befehl der Wörter besteht /bin/ls und arf, und die doppelten Anführungszeichen betont werden, dass wirklich /bin/ls ein einziges Wort ist.

Wenn Sie

$ eval '"/bin/ls" arf' 

geben Sie sagen Bash, dass der Befehl der Wörter besteht eval und "/bin/ls" arf. Da der Zweck der eval ist, so zu tun, dass ihr Argument ist ein tatsächlicher Mensch-Eingabebefehl, ist dies äquivalent zu laufen

$ "/bin/ls" arf 

und die " genau wie bei der Eingabeaufforderung verarbeitet wird.

Beachten Sie, dass dieser Vorwand spezifisch für eval ist; Bash tut normalerweise nichts, um vorzutäuschen, dass etwas ein menschliches Kommando ist.

Bei der Eingabe von

$ c='"/bin/ls" arf' 
$ $c 

die $c wird ersetzt, und erfährt dann Wort Spaltung (siehe §3.5.7 "Word Splitting" in the Bash Reference Manual), so die Worte des Befehls sind "/bin/ls" (man beachte die Anführungszeichen!) Und arf. Es ist unnötig zu sagen, dass dies nicht funktioniert. (Es ist auch nicht sehr sicher, da neben der Wortteilung $c auch eine Dateinamenerweiterung erfährt und was nicht. Im Allgemeinen sollten Ihre Parametererweiterungen immer in Anführungszeichen stehen, und wenn sie nicht sein können, sollten Sie Ihre neu schreiben So können sie sein.Unquoted-Parameter-Erweiterungen erfordern Ärger.

)

Wenn Sie

$ c='"/bin/ls" arf' 
$ $($c) 

Typ ist dies das gleiche wie vorher, nur dass jetzt bist du auch die Ausgabe des arbeitsfreien Befehl als neuen Befehl zu verwenden versuchen. Unnötig zu sagen, dass der nicht arbeitende Befehl nicht plötzlich funktioniert.

Als Ignacio Vazquez-Abrams sagt in seiner Antwort die richtige Lösung ist eine Anordnung zu verwenden, und behandeln die Angabe richtig:

$ c=("/bin/ls" arf) 
$ "${c[@]}" 

, die mit zwei Elementen c auf ein Array setzt, /bin/ls und arf, und verwendet diese beiden Elemente als das Wort eines Befehls.

+0

(hasse das 5-Minuten-Limit) Diese letzte Zeile zeigt nicht die Ausführung eines gepackten Befehls in einer Form, in der die Ausgabe erfasst werden könnte (was in der ursprünglichen Frage nicht erwähnt wird) Versuchen Sie c = ("/ bin/ls" "/ bin/l * ") und c = ("/bin/ls "/ bin/l *) mit echo" '$ {c [@]}' ". Mit dieser Methode kann ich kein Array mit in Anführungszeichen gesetzten Argumenten zur späteren Verwendung mit $() ... erstellen. – Shenme

+1

@Shenme: Soviel ich sagen kann, was du sagst ist: "Ich möchte' eval' verwenden, weil ich eine beliebige Zeichenfolge als Bash-Befehl verarbeiten möchte. " Was alle anderen sagen, ist "eval" ist böse, weil es eine beliebige Zeichenfolge als Bash-Befehl verarbeitet."Sie versuchen, diese Standpunkte miteinander in Einklang zu bringen, indem Sie fragen:" Gibt es eine * nicht * -mörderische Möglichkeit, dieses böse Ding zu tun? ", Aber das wird nicht funktionieren. Die Standpunkte sind grundsätzlich unvereinbar. Um etwas Böses zu tun, verwenden Sie ein böses Werkzeug, oder, um das böse Werkzeug zu vermeiden, schreibe dein Skript um, um das böse Ding zu vermeiden – ruakh

+0

Nein, keine willkürlichen Befehle, keine bösen Absichten :-) Ich wollte nur einen Befehl aufbauen und ihn kontrolliert ausführen Ich werde die Eingaben kontrollieren und bin sogar paranoid genug, um '-' zu verwenden. Die Leute sehen die vereinfachenden Antworten auf komplexe Meta-Fragen und kritisieren Sie, wenn Sie 'eval' verwenden, also wollte ich die Kritik vermeiden. - (Wie auch immer, z = $ ($ {c [@]}) funktioniert ganz gut. – Shenme

2

Vom man page for bash in Bezug auf eval:

eval [arg ...]: Die args werden gelesen und in einem einzigen Befehl verkettet. Dieser Befehl wird dann von der Shell gelesen und ausgeführt, und sein Exit-Status wird als Wert von eval zurückgegeben.

Wenn c als "\"/bin/ls\" arf" definiert ist, werden die äußeren Anführungszeichen verursachen die ganze Sache bis zu eval als erstes Argument verarbeitet werden, die voraussichtlich einen Befehl oder ein Programm sein. Sie müssen Ihre eval Argumente so übergeben, dass der Zielbefehl und seine Argumente separat aufgelistet werden. Das $(...) Konstrukt verhält sich anders als eval, weil es kein Befehl ist, der Argumente übernimmt. Es kann den gesamten Befehl auf einmal verarbeiten, anstatt nacheinander die Argumente zu verarbeiten.

Eine Anmerkung zu Ihrer ursprünglichen Prämisse: Der Hauptgrund, dass Leute sagen, dass eval böse ist, war, weil es häufig von Skripten verwendet wird, um einen vom Benutzer bereitgestellten String als Shell-Befehl auszuführen. Obwohl dies zuweilen ein Problem darstellt, ist dies ein Sicherheitsproblem major (es gibt normalerweise keine praktische Möglichkeit, die Zeichenfolge vor der Ausführung zu überprüfen). Das Sicherheitsproblem tritt nicht auf, wenn Sie eval auf fest codierten Zeichenfolgen in Ihrem Skript verwenden, wie Sie es tun. Es ist jedoch normalerweise einfacher und sauberer, innerhalb von Skripten $(...) oder `...` für die Befehlsersetzung zu verwenden, so dass kein echter Anwendungsfall für eval übrig bleibt.

+0

Zusätzlicher Tipp: Wenn Sie versuchen zu sehen, wie ein erweiterter Befehl von der Shell gesehen wird, kann die Verwendung von 'set -v' manchmal genauere Ergebnisse liefern als' echo'. – bta

+0

so ist eval sicher, wenn die ausgewertete Zeichenkette in keiner Weise den Benutzereingaben ausgesetzt ist; aber wenn wir zu irgendeinem Zeitpunkt daran scheitern, dies zu verhindern, kann es ausgenutzt werden; die Verwendung von '' '' '' '' '' '' '' '' ('' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' Danke! Das war der spezifische Tipp, nach dem ich gesucht habe, bevor ich meine Skripte ändere, hehe! :) –