Wie führt man einen beliebigen nativen Befehl aus einer Zeichenfolge aus?

208

Ich kann meine Notwendigkeit mit dem folgenden Szenario ausdrücken: Schreiben Sie eine Funktion, die eine Zeichenfolge akzeptiert, die als nativer Befehl ausgeführt werden soll.

Eine Idee ist nicht allzu weit hergeholt: Wenn Sie mit anderen Befehlszeilenprogrammen von anderen Stellen im Unternehmen zusammenarbeiten, die Ihnen einen Befehl zum wörtlichen Ausführen liefern. Da Sie den Befehl nicht steuern, müssen Sie einen gültigen Befehl als Eingabe akzeptieren . Dies sind die Hauptprobleme, die ich nicht leicht überwinden konnte:

  1. Der Befehl kann ein Programm ausführen, das in einem Pfad mit einem Leerzeichen darin lebt:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
  2. Der Befehl kann Parameter mit Leerzeichen enthalten:

    $command = 'echo "hello world!"';
  3. Der Befehl kann sowohl einfache als auch doppelte Häkchen haben:

    $command = "echo `"it`'s`"";

Gibt es einen sauberen Weg, dies zu erreichen? Ich war nur in der Lage, aufwändige und hässliche Problemumgehungen zu entwickeln, aber für eine Skriptsprache sollte dies meiner Meinung nach kinderleicht sein.

Johnny Kauffman
quelle

Antworten:

318

Invoke-Expression, auch aliased als iex. Das Folgende wird an Ihren Beispielen Nr. 2 und Nr. 3 funktionieren:

iex $command

Einige Zeichenfolgen werden nicht unverändert ausgeführt, z. B. Beispiel 1, da die Exe in Anführungszeichen steht. Dies funktioniert unverändert, da der Inhalt der Zeichenfolge genau so ist, wie Sie ihn direkt an einer Powershell-Eingabeaufforderung ausführen würden:

$command = 'C:\somepath\someexe.exe somearg'
iex $command

Wenn sich die Exe jedoch in Anführungszeichen befindet, benötigen Sie die Hilfe von &, um sie wie in diesem Beispiel als über die Befehlszeile ausgeführt auszuführen:

>> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"

Und dann im Drehbuch:

$command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
iex "& $command"

Wahrscheinlich können Sie fast alle Fälle behandeln, indem Sie feststellen, ob das erste Zeichen der Befehlszeichenfolge "wie in dieser naiven Implementierung lautet :

function myeval($command) {
    if ($command[0] -eq '"') { iex "& $command" }
    else { iex $command }
}

Möglicherweise finden Sie jedoch auch andere Fälle, die auf andere Weise aufgerufen werden müssen. In diesem Fall müssen Sie entweder try{}catch{}die Befehlszeichenfolge verwenden , möglicherweise für bestimmte Ausnahmetypen / Nachrichten, oder die Befehlszeichenfolge untersuchen.

Wenn Sie immer absolute Pfade anstelle relativer Pfade erhalten, sollten Sie nicht viele Sonderfälle außerhalb der oben genannten 2 haben.

Joel B Fant
quelle
3
Aliasing ist großartig. Denken Sie daran, dass der Alias ​​wahrscheinlich nicht eingerichtet wird, wenn Sie auf einen anderen Computer wechseln oder dieses Skript an eine andere Person senden. Bevorzugen Sie die vollständigen Namen der PowerShell-Funktionen.
Doug Finke
1
@Doug: Meistens mache ich das oder verwende die eingebauten Aliase (besonders der Kürze halber in der Kommandozeile). Die evalDinge sind halb im Scherz, weil es in so vielen anderen Skriptsprachen so heißt, und dies ist nicht die erste Frage, die ich gesehen habe, von der jemand keine Ahnung hatte invoke-expression. Und der Fall des OP klingt nur nach einem internen Skript.
Joel B Fant
Ich habe es versucht, aber es funktioniert nicht mit: $ command = '"C: \ Programme \ Windows Media Player \ mplayer2.exe" "H: \ Audio \ Musik \ Stevie Wonder \ Stevie Wonder - Superstition.mp3" '
Johnny Kauffman
3
Möglicherweise müssen Sie ein "&" oder "." Zeichen vor dem eigentlichen Befehl, wenn es nicht Powershell-native ist, zBInvoke-Expression "& $command"
Torbjörn Bergstedt
Torbjörn Bergstedt gewinnt! Das magische Extra "&" hat mein Problem gelöst! Infolgedessen bin ich sowohl glücklich als auch nervös.
Johnny Kauffman
19

In diesem Microsoft Connect-Bericht erfahren Sie im Wesentlichen, wie schwierig es ist, PowerShell zum Ausführen von Shell-Befehlen zu verwenden (oh, die Ironie).

http://connect.microsoft.com/PowerShell/feedback/details/376207/

Sie schlagen vor, --%PowerShell zu zwingen, nicht mehr zu versuchen, den Text rechts zu interpretieren.

Beispielsweise:

MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj
Luke Puplett
quelle
1
Ah, und Microsoft hat das Internet kaputt gemacht und der Link ist nicht mehr gültig.
Johan Boulé
1
Der obige
mwfearnley
4

Die akzeptierte Antwort funktionierte bei mir nicht, als ich versuchte, die Registrierung auf Deinstallationszeichenfolgen zu analysieren und auszuführen. Es stellte sich heraus, dass ich den Anruf doch nicht brauchte Invoke-Expression.

Endlich bin ich auf diese nette Vorlage gestoßen, um zu sehen, wie man Deinstallationszeichenfolgen ausführt:

$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$app = 'MyApp'
$apps= @{}
Get-ChildItem $path | 
    Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} | 
    ForEach-Object -process {$apps.Set_Item(
        $_.getvalue('UninstallString'),
        $_.getvalue('DisplayName'))
    }

foreach ($uninstall_string in $apps.GetEnumerator()) {
    $uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
    & $uninstall_app $uninstall_arg
}

Dies funktioniert für mich, weil $appes sich um eine interne Anwendung handelt, von der ich weiß, dass sie nur zwei Argumente hat. Für komplexere Deinstallationszeichenfolgen möchten Sie möglicherweise den Join-Operator verwenden . Außerdem habe ich gerade eine Hash-Map verwendet, aber eigentlich möchten Sie wahrscheinlich ein Array verwenden.

Wenn Sie mehrere Versionen derselben Anwendung installiert haben, durchläuft dieses Deinstallationsprogramm alle auf einmal, was verwirrend MsiExec.exeist.

Droogans
quelle
1

Wenn Sie den Aufrufoperator verwenden möchten, können die Argumente ein Array sein, das in einer Variablen gespeichert ist:

$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs

Der Aufrufoperator arbeitet auch mit ApplicationInfo-Objekten.

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
js2010
quelle