¿Cómo se ejecuta un comando nativo arbitrario desde una cadena?


Puedo expresar mi necesidad con el siguiente escenario: Escribir una función que acepte una cadena para ser ejecutada como un comando nativo.

No es una idea demasiado descabellada: si está interactuando con otras utilidades de línea de comandos de otro lugar de la compañía que le proporcionan un comando para ejecutar verbatim. Debido a que no controla el comando, necesita aceptar cualquier comando válido como entrada. Estos son los principales contratiempos que he sido incapaz de fácilmente superar:

  1. El comando podría ejecutar un programa que vive en una ruta con un espacio en ella:

    $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 puede tener marcas simples y dobles:

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

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

Author: SteveC, 2011-06-14

3 answers

Invoke-Expression, también alias iex. Lo siguiente funcionará en sus ejemplos # 2 y # 3:

iex $command

Algunas cadenas no se ejecutarán tal cual, como tu ejemplo #1 porque el exe está entre comillas. Esto funcionará como está, porque el contenido de la cadena es exactamente como se ejecutaría directamente desde un símbolo del sistema de Powershell:

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

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

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

Y luego en el script:

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

Probablemente, podría manejar casi todos los casos detectando si el primer carácter de la cadena de comandos es ", como en esta implementación ingenua:

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

Pero usted puede encontrar algunos otros casos que tienen que ser invocados de una manera diferente. En ese caso, necesitará 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 hay, fuera de los 2 anteriores.

 240
Author: Joel B Fant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-14 13:52:33

Por favor, también vea este informe de Microsoft Connect sobre esencialmente, lo blummin' difícil que es usar PowerShell para ejecutar comandos de shell (oh, la ironía).

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

Sugieren usar --% como una forma de forzar a PowerShell a dejar de intentar 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
 16
Author: Luke Puplett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-18 21:32:16

La respuesta aceptada no estaba funcionando para mí cuando intentaba analizar el registro para desinstalar cadenas y ejecutarlas. Resulta que no necesitaba la llamada a Invoke-Expression después de todo.

Finalmente me encontré con esta bonita plantilla para ver cómo ejecutar cadenas 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í, es decir, porque $app es una aplicación interna que sé que solo tendrá dos argumentos. Para cadenas de desinstalación más complejas, puede usar el operador join. Además, acabo de usar un mapa hash, pero en realidad, probablemente quieras usar una matriz.

Además, si tiene varias versiones de la misma aplicación instalada, este desinstalador las recorrerá todas a la vez, lo que confunde MsiExec.exe, por lo que también está eso.

 4
Author: Droogans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-11 15:44:10