2012-01-06 21 views
61

¿Hay algún error en el comando Start-Process de Powershell al acceder a las propiedades StandardError y StandardOutput?Captura de error estándar y error con Start-Process

Si corro el siguiente me sale ninguna salida

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait 
$process.StandardOutput 
$process.StandardError 

Pero si vuelvo a dirigir la salida a un archivo que he obtener el resultado esperado

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt 
+4

En este caso específico, ¿realmente necesita iniciar el proceso? ...'$ process = ping localhost' # guardará el resultado en la variable de proceso. – mjsr

+1

Es cierto. Estaba buscando una forma más limpia de manejar el retorno y los argumentos. Terminé escribiendo el guión como lo mostraste. – jzbruno

Respuesta

84

Eso es como Start-Process fue diseñada por alguna razón. He aquí una manera de conseguirlo sin necesidad de enviar a archivo:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo 
$pinfo.FileName = "ping.exe" 
$pinfo.RedirectStandardError = $true 
$pinfo.RedirectStandardOutput = $true 
$pinfo.UseShellExecute = $false 
$pinfo.Arguments = "localhost" 
$p = New-Object System.Diagnostics.Process 
$p.StartInfo = $pinfo 
$p.Start() | Out-Null 
$p.WaitForExit() 
$stdout = $p.StandardOutput.ReadToEnd() 
$stderr = $p.StandardError.ReadToEnd() 
Write-Host "stdout: $stdout" 
Write-Host "stderr: $stderr" 
Write-Host "exit code: " + $p.ExitCode 
+6

Estoy aceptando su respuesta. Desearía que no hubieran creado propiedades que no se usan, es muy confuso. – jzbruno

+1

@jzbruno El equipo de PowerShell no creó las propiedades StandardOutput/StandardError ... Forman parte del [System.Diagnostics.Process] subyacente (http://msdn.microsoft.com/en-us/library/system). objeto diagnosticics.process.aspx). Sin embargo, solo están disponibles cuando la propiedad 'UseShellExecute' está establecida en falso. Así que depende de cómo el equipo de PowerShell implementó 'Start-Process' detrás de las escenas ... Desafortunadamente no puedo ver el código fuente :-( –

+2

Si tiene problemas para ejecutar un proceso de esta manera, consulte la respuesta aceptada aquí http: //stackoverflow.com/questions/11531068/powershell-capturing-standard-out-and-error-with-process-object, que tiene una ligera modificación en WaitForExit y StandardOutput.ReadTo End –

13

en el código dado en la pregunta, creo que la lectura de la propiedad ExitCode de la variable de iniciación debe trabajar.

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait 
$process.ExitCode 

Tenga en cuenta que (como en el ejemplo) es necesario agregar la -PassThru y params -wait (esto me tomó por un tiempo)

+0

¿Qué pasa si argumentlist contiene una variable? No parece expandirse. –

+1

pondría la lista de argumentos entre comillas. Funcionaría eso ? ... $ process = Start-Process -FilePath ping -ArgumentList "-t localhost -n 1" -NoNewWindow -PassThru -Wait – JJones

8

También tuve este problema y terminó usando código Andys para crear una función para limpiar cosas cuando se deben ejecutar varios comandos, devolverá stderr, stdout y los códigos de salida como objetos. Una cosa para tener en cuenta que la función no aceptará. \ En la ruta, se deben usar rutas completas.

Function Execute-Command ($commandTitle, $commandPath, $commandArguments) 
{ 
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo 
    $pinfo.FileName = $commandPath 
    $pinfo.RedirectStandardError = $true 
    $pinfo.RedirectStandardOutput = $true 
    $pinfo.UseShellExecute = $false 
    $pinfo.Arguments = $commandArguments 
    $p = New-Object System.Diagnostics.Process 
    $p.StartInfo = $pinfo 
    $p.Start() | Out-Null 
    $p.WaitForExit() 
    [pscustomobject]@{ 
     commandTitle = $commandTitle 
     stdout = $p.StandardOutput.ReadToEnd() 
     stderr = $p.StandardError.ReadToEnd() 
     ExitCode = $p.ExitCode 
    } 
} 

aquí es cómo usarlo

$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0" 
+0

Buena idea, pero parece que la sintaxis no funciona para mí. ¿No debería la lista de parámetros usar la sintaxis param ([tipo] $ ArgumentName)? ¿Puedes agregar una llamada de ejemplo a esta función? – Lockszmith

4

que realmente tenía problemas con los ejemplos anteriores de @Andy Arismendi y @LPG. Siempre se debe utilizar:

$stdout = $p.StandardOutput.ReadToEnd() 

antes de llamar

$p.WaitForExit() 

ejemplo completo es:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo 
$pinfo.FileName = "ping.exe" 
$pinfo.RedirectStandardError = $true 
$pinfo.RedirectStandardOutput = $true 
$pinfo.UseShellExecute = $false 
$pinfo.Arguments = "localhost" 
$p = New-Object System.Diagnostics.Process 
$p.StartInfo = $pinfo 
$p.Start() | Out-Null 
$stdout = $p.StandardOutput.ReadToEnd() 
$stderr = $p.StandardError.ReadToEnd() 
$p.WaitForExit() 
Write-Host "stdout: $stdout" 
Write-Host "stderr: $stderr" 
Write-Host "exit code: " + $p.ExitCode 
+0

¿Resuelve esto el problema de interbloqueo? –

+0

¿Dónde leyó que "Siempre debe usar: $ p.StandardOutput.ReadToEnd() before $ p.WaitForExit()"? Si hay salida en el búfer que se agota, después de más salida en un momento posterior, se perderá si la línea de ejecución está en WaitForExit y el proceso no ha finalizado (y posteriormente genera más stderr o stdout) .... – CJBS

+0

Con respecto a mi comentario anterior, más tarde vi los comentarios sobre la respuesta aceptada sobre el bloqueo y el desbordamiento de búfer en casos de gran producción, pero aparte de eso, esperaría que solo porque el búfer se lee hasta el final, no significa que el proceso se ha completado, y podría haber más resultados que se han perdido. ¿Me estoy perdiendo de algo? – CJBS

3

IMPORTANTE:

Hemos estado utilizando la función de lo indicado anteriormente por LPG. Sin embargo, esto contiene un error que puede encontrar al iniciar un proceso que genera una gran cantidad de resultados. Debido a esto, puede terminar con un interbloqueo al utilizar esta función. En lugar de utilizar la versión adaptada a continuación:

Function Execute-Command ($commandTitle, $commandPath, $commandArguments) 
{ 
    Try { 
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo 
    $pinfo.FileName = $commandPath 
    $pinfo.RedirectStandardError = $true 
    $pinfo.RedirectStandardOutput = $true 
    $pinfo.UseShellExecute = $false 
    $pinfo.Arguments = $commandArguments 
    $p = New-Object System.Diagnostics.Process 
    $p.StartInfo = $pinfo 
    $p.Start() | Out-Null 
    [pscustomobject]@{ 
     commandTitle = $commandTitle 
     stdout = $p.StandardOutput.ReadToEnd() 
     stderr = $p.StandardError.ReadToEnd() 
     ExitCode = $p.ExitCode 
    } 
    $p.WaitForExit() 
    } 
    Catch { 
    exit 
    } 
} 

Para más información sobre este tema se puede encontrar at MSDN:

Una condición de interbloqueo puede resultar si el proceso padre llama p.WaitForExit antes y p.StandardError.ReadToEnd el proceso secundario escribe suficiente texto para completar la secuencia redirigida. El proceso principal esperaría indefinidamente para que el proceso secundario salga. El proceso secundario esperaría indefinidamente para que el padre lea de la secuencia StandardError completa.

EDITAR: Agregó una llave al cuello faltante al final del bloque Try.

+1

Este código todavía se bloquea debido a la llamada síncrona a ReadToEnd(), que también describe su enlace a MSDN. – bergmeister

+0

Esto ahora parece haber resuelto mi problema. Debo admitir que no entiendo completamente por qué se colgó, pero parece que el stderr vacío bloqueó el proceso para terminar. Algo extraño, ya que funcionó durante un largo período de tiempo, pero de repente justo antes de Navidad comenzó a fallar, lo que provocó que muchos procesos de Java se bloquearan. – rhellem

Cuestiones relacionadas