2011-06-14 11 views
136

Puedo expresar mi necesidad con el siguiente escenario: Escriba una función que acepte una cadena para ejecutar como un comando nativo.¿Cómo se ejecuta un comando nativo arbitrario desde una cadena?

No es demasiado exagerado una idea: si está interactuando con otras utilidades de línea de comandos de otra parte de la empresa que le proporciona un comando para ejecutar al pie de la letra. Como no controla el comando, necesita aceptar cualquier comando válido como entrada. Estos son los principales contratiempos que he podido superar fácilmente:

  1. El comando podría ejecutar un programa de vida en un camino con un espacio en el que:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello'; 
    
  2. El comando puede tener parámetros con espacios en ellos:

    $command = 'echo "hello world!"'; 
    
  3. El comando podría tener garrapatas, tanto individuales y dobles:

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

¿Hay alguna forma limpia de lograr esto? Solo he sido capaz de idear soluciones escandalosas y feas, pero para un lenguaje de scripting siento que esto debería ser completamente simple.

Respuesta

211

Invoke-Expression, también con alias como iex. El siguiente trabajo de sus ejemplos # 2 y # 3:

iex $command 

Algunas cadenas no se ejecutarán tal cual, como su ejemplo # 1 porque el exe está entre comillas. Esto funcionará tal cual, ya que los contenidos de la cadena son exactamente cómo se ejecuta directamente desde la línea de comandos de PowerShell:

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

Sin embargo, si el exe está entre comillas, necesita la ayuda de & para ponerlo en marcha, como en este ejemplo, como se ejecuta desde la línea de comandos:

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

Y luego, en la secuencia de comandos:

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

Probablemente, usted podría manejar casi todos los casos por detectar si el primer carácter de la cadena de mando es ", al igual que en esta implementación ingenua:

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

pero puede encontrar algunos otros casos que tienen que ser invocado de una manera diferente. En ese caso, deberá usar try{}catch{}, quizás para tipos/mensajes de excepción específicos, o examinar la cadena de comandos.

Si siempre recibe rutas absolutas en lugar de rutas relativas, no debería tener muchos casos especiales, si los hubiera, fuera de los 2 anteriores.

+2

Aliasing es grande. Recuerde, si se muda a otra máquina o envía esa secuencia de comandos a otra persona, ese alias probablemente no se configure. Prefiere los nombres completos de las funciones de PowerShell. –

+0

@Doug: la mayor parte del tiempo lo hago o uso los alias incorporados (especialmente por brevedad en la línea de comandos). Las cosas 'eval' son medio bromas porque así se llama en muchos otros lenguajes de scripting, y esta no es la primera pregunta que he visto donde alguien no tenía idea acerca de' invoke-expression'. Y el caso del OP suena como el guión interno solamente. –

+0

He intentado esto, pero no funciona con: $ command = '"C: \ Archivos de programa \ Windows Media Player \ mplayer2.exe" "H: \ Audio \ Music \ Stevie Wonder \ Stevie Wonder - Superstition. mp3 "' –

3

La respuesta aceptada no me funcionaba cuando intentaba analizar las cadenas de desinstalación del registro y ejecutarlas. Resulta que no necesitaba la llamada al Invoke-Expression después de todo.

finalmente me encontré con este nice template para ver cómo ejecutar secuencias de desinstalación:

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

Esto funciona para mí, esto es debido a $app es una aplicación en casa, que yo sepa sólo tendrá dos argumentos. Para cadenas de desinstalación más complejas, es posible que desee utilizar el join operator. Además, acabo de utilizar un hash-map, pero en realidad, probablemente quieras usar una matriz.

Además, si tiene instaladas varias versiones de la misma aplicación, este desinstalador realizará un ciclo a través de todas ellas a la vez, lo que confunde MsiExec.exe, por lo que también existe.

14

También vea este informe de Microsoft Connect esencialmente sobre cuán difícil es usar PowerShell para ejecutar comandos de shell (¡oh, la ironía!).

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

Sugieren usar --% como una manera de forzar PowerShell para dejar de tratar de interpretar el texto a la derecha.

Por ejemplo:

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