Come si esegue un comando nativo arbitrario da una stringa?


208

Posso esprimere la mia necessità con il seguente scenario: Scrivere una funzione che accetti una stringa da eseguire come comando nativo.

Non è troppo preso da un'idea: se stai interfacciarsi con altre utility da riga di comando da altre parti dell'azienda che ti forniscono un comando per eseguire testualmente. Poiché non si controlla il comando, è necessario accettare qualsiasi comando valido come input . Questi sono i principali singhiozzi che non sono stato in grado di superare facilmente:

  1. Il comando potrebbe eseguire un programma che vive in un percorso con uno spazio al suo interno:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
  2. Il comando può avere parametri con spazi in essi:

    $command = 'echo "hello world!"';
  3. Il comando potrebbe avere segni di spunta sia singoli che doppi:

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

C'è un modo pulito per ottenere questo risultato? Sono stato solo in grado di escogitare soluzioni sontuose e sgradevoli, ma per un linguaggio di scripting penso che questo dovrebbe essere completamente semplice.

Risposte:


318

Invoke-Expression, anche alias come iex. Quanto segue funzionerà con i tuoi esempi n. 2 e n. 3:

iex $command

Alcune stringhe non verranno eseguite così come sono, ad esempio il tuo esempio n. 1 perché l'exe è tra virgolette. Funzionerà così com'è, perché il contenuto della stringa è esattamente come lo faresti direttamente dal prompt dei comandi di Powershell:

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

Tuttavia, se l'exe è tra virgolette, è necessario l'aiuto di &per farlo funzionare, come in questo esempio, come eseguito dalla riga di comando:

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

E poi nella sceneggiatura:

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

Probabilmente, potresti gestire quasi tutti i casi rilevando se il primo carattere della stringa di comando è ", come in questa ingenua implementazione:

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

Ma potresti trovare altri casi che devono essere invocati in un modo diverso. In tal caso, dovrai utilizzare try{}catch{}, magari per specifici tipi / messaggi di eccezione, oppure esaminare la stringa di comando.

Se ricevi sempre percorsi assoluti anziché percorsi relativi, non dovresti avere molti casi speciali, al di fuori di quelli sopra 2.


3
L'aliasing è fantastico. Ricorda, se ti sposti su un altro computer o invii quello script a qualcun altro, questo alias probabilmente non verrà impostato. Preferire i nomi completi delle funzioni di PowerShell.
Doug Finke

1
@Doug: Il più delle volte lo faccio o uso gli alias integrati (specialmente per brevità sulla riga di comando). Il fatto evalè scherzoso perché è così che viene chiamato in così tanti altri linguaggi di scripting, e questa non è la prima domanda che ho visto di cui qualcuno non aveva idea invoke-expression. E il caso del PO suona solo come uno script interno.
Joel B Fant,

Ho provato questo, ma non funziona con: $ command = '"C: \ Programmi \ Windows Media Player \ mplayer2.exe" "H: \ Audio \ Music \ Stevie Wonder \ Stevie Wonder - Superstition.mp3" '
Johnny Kauffman,

3
Potrebbe essere necessario inserire un "&" o "." firmare prima del comando effettivo se non è nativo di PowerShell, ad es.Invoke-Expression "& $command"
Torbjörn Bergstedt

Vince Torbjörn Bergstedt! Il magico extra "&" ha risolto il mio problema! Di conseguenza, sono felice e agitato.
Johnny Kauffman,

19

Si prega di vedere anche questo rapporto di Microsoft Connect essenzialmente su quanto sia difficile usare PowerShell per eseguire comandi shell (oh, l'ironia).

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

Suggeriscono di usare --%come un modo per forzare PowerShell a smettere di provare a interpretare il testo a destra.

Per esempio:

MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj

1
Ah, e Microsoft ha rotto Internet e il collegamento non è più valido.
Johan Boulé,

1
Il link sopra è su archive.org all'indirizzo web.archive.org/web/20131122050220/http://…
mwfearnley,

4

La risposta accettata non funzionava per me quando cercavo di analizzare il registro per disinstallare le stringhe ed eseguirle. Dopo tutto, non ho avuto bisogno della chiamata Invoke-Expression.

Finalmente mi sono imbattuto in questo bel modello per vedere come eseguire le stringhe di disinstallazione:

$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
}

Questo funziona per me, vale a dire perché $appè un'applicazione interna che conosco avrà solo due argomenti. Per stringhe di disinstallazione più complesse, ti consigliamo di utilizzare l' operatore join . Inoltre, ho appena usato una mappa hash, ma in realtà probabilmente vorresti usare un array.

Inoltre, se hai più versioni della stessa applicazione installata, questo programma di disinstallazione le scorrerà tutte in una volta, il che confonde MsiExec.exe, quindi c'è anche quello.


1

Se si desidera utilizzare l'operatore di chiamata, gli argomenti possono essere una matrice memorizzata in una variabile:

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

L'operatore di chiamata funziona anche con oggetti ApplicationInfo.

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.