Start-Process en PowerShell: Inicio intuitivo de procesos

Para entender por qué Start-Process y todos estos otros comandos están limitados, primero debes entender cómo funciona un archivo EXE típico. Cuando se ejecuta un archivo EXE, realiza la acción para la cual fue diseñado; hace ping a algo (ping), inicia un instalador de software (setup), busca algún registro DNS (nslookup), lo que sea. Para esta parte y Start-Process y otros comandos para iniciar un proceso funcionan muy bien. Es simple. La limitación ocurre cuando ese EXE devuelve alguna salida.

Podemos iniciar un proceso en PowerShell de muchas formas diferentes. Tenemos los cmdlets PowerShell Start-Process e Invoke-Expression, podemos llamar directamente al ejecutable o usar el ampersand (&) para invocar expresiones. La forma más común es usar Start-Process porque probablemente es la más intuitiva. PowerShell es conocido por su enfoque intuitivo en la nomenclatura de comandos y Start-Process es una excelente opción. Sin embargo, ese comando está limitado.

Flujos

Un archivo EXE, al igual que muchos otros archivos ejecutables no limitados a Windows, tiene un concepto de flujos estándar. Los flujos estándar son la forma en que los archivos ejecutables devuelven la salida. Estos flujos vienen en tres variantes: stdin, stdout y stderr. Stdin es el flujo que puede pasar al ejecutable, en lo que no nos enfocaremos aquí. Stdout es el flujo que el ejecutable utiliza para enviar salida normal sin errores.

En términos de PowerShell, piensa en stdout como lo que devuelve Write-Output. Cuando ocurre un error (dependiendo de si el desarrollador del ejecutable lo escribió correctamente), el ejecutable debería devolver la salida a través de stderr. Este es un flujo que se reserva para devolver cualquier información de error.

Estos flujos permiten a los usuarios de estos archivos ejecutables diferenciar entre lo que es una salida típica y lo que es un error. Los flujos han existido durante décadas y Windows, por lo tanto, PowerShell sabe todo sobre ellos y han adoptado los suyos propios.

Códigos de salida

Un código de salida, código de retorno o código de resultado es otro concepto antiguo que siguen los archivos ejecutables. Un código de salida es un entero que permite al ejecutable señalar un estado al usuario cuando sale.

Hay algunos códigos de salida estándar que se supone que los programas siguen, como si el código de salida es 0, todo está bien, el código de salida 3010 significa que necesita un reinicio, y así sucesivamente. PowerShell puede capturar este código de salida de varias maneras, como usar la variable automática $LastExitCode. Los códigos de salida son otro método mediante el cual el archivo ejecutable se comunica con el usuario.

Capturando Flujos y Códigos de Salida

Ahora que entiendes con qué estamos trabajando, usemos un ejemplo. Para mantenerlo simple, usaré el buen viejo ping.exe. Primero, haré ping a google.com, lo que devolverá un resultado exitoso. No estamos usando PowerShell para iniciar el proceso aquí.

PS C:\> ping google.com
Pinging google.com [2607:f8b0:4004:80b::200e] with 32 bytes of data:

Reply from 2607:f8b0:4004:80b::200e: time=61ms
Reply from 2607:f8b0:4004:80b::200e: time=65ms
Reply from 2607:f8b0:4004:80b::200e: time=80ms
Reply from 2607:f8b0:4004:80b::200e: time=78ms

Ping statistics for 2607:f8b0:4004:80b::200e:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 61ms, Maximum = 80ms, Average = 71ms

PS> $LastExitCode
0
PS> $?
True

I’ll now purposefully ping a host that doesn’t resolve a DNS name and will fail. Notice the value of $LastExitCode.

PS> ping googlllll.com

Ping request could not find host googlllll.com. Please check the name and try again.
PS> $LASTEXITCODE
1

Viste que el código de salida difiere porque el resultado del ping fue diferente, pero no viste flujos porque nativamente PowerShell no entiende la diferencia cuando se trata de archivos ejecutables. El texto que ves en la consola es blanco; no es el texto de error rojo que conoces y amas.

Podemos capturar los flujos y redirigirlos a archivos de algunas formas diferentes, como usar > y 2> como se muestra a continuación. Esta es la forma antigua.

PS> ping googllll.com > output.msg 2> output.err
PS> Get-Content .\output.err
PS> Get-Content .\output.msg

Ping request could not find host googllll.com. Please check the name and try again.

Observa, sin embargo, que, en este caso, aunque ping.exe está devolviendo un código de salida de 1 (como se muestra arriba), esa salida aún va a stdout. Esto es común. El flujo y el código de salida que se devuelven dependen completamente del desarrollador y vienen en muchos estilos diferentes, desafortunadamente.

Si prefieres ir por la “nueva escuela”, podrías usar Start-Process y usar los parámetros -RedirectStandardError y -RedirectStandardOutput, pero aún así solo irían a archivos.

Limitaciones de Start-Process

Puedes ver que iniciar un proceso y devolver alguna salida común no es demasiado común. Además, PowerShell no maneja muy bien los flujos y los códigos de salida. Si quisiera usar Start-process para ejecutar ping.exe y quisiera rastrear lo que sucedió en el resultado, tendría que hacer algo como esto cada vez que quisiera ejecutar un archivo ejecutable.

PS> Start-Process -FilePath -NoNewWindow ping -ArgumentList 'google.com' -RedirectStandardOutput output.txt -RedirectStandardError err.txt
PS> $LastExitCode
PS> Get-Content -Path output.txt
PS> Get-Content -Path err.txt

I still wouldn’t get my error text either!

Arreglemos todo esto. Descarga una pequeña función que creé llamada Invoke-Process desde la PowerShell Gallery.

PS> Install-Script 'Invoke-Process'
PS> . Invoke-Process.ps1

Ahora ejecuta ping usando un host válido y mira qué sucede.

Invoke-Process -FilePath ping -ArgumentList ‘google.com’

Observa cómo obtenemos la misma salida. ¡Esto es bueno!

Ahora ejecuta ping usando un host no válido.

Invoke-Process -FilePath ping -ArgumentList ‘googlddde.com’

Ahora observa que obtenemos el típico texto de error rojo como esperaríamos si algo saliera mal. Invoke-Process utiliza Start-Process bajo la superficie para capturar stdout, stderr y el código de salida, y luego envía la salida a cualquier flujo al que se supone que debe ir. ¡Así es como debería ser!

Lástima que PowerShell no lo hizo más fácil trabajar con flujos y códigos de salida de archivos ejecutables, pero supongo que hoy en día, podemos agregar eso ahora que es de código abierto.

Source:
https://adamtheautomator.com/start-process/