Me gustaría ejecutar un proceso externo y capturar su salida de comando a una variable en PowerShell. Actualmente estoy usando esto:
$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -WaitHe confirmado que el comando se está ejecutando, pero necesito capturar la salida en una variable. Esto significa que no puedo usar -RedirectOutput porque esto solo redirige a un archivo.
                    
                        powershell
                                process
                                
                    
                    
                        Adam Bertram
fuente
                
                fuente

Start-Processpara ejecutar (por definición externo) aplicaciones de consola de forma síncrona; simplemente invoque directamente , como en cualquier shell; a saber:netdom /verify $pc /domain:hosp.uhhg.org. Al hacerlo, la aplicación se conecta a las transmisiones estándar de la consola de llamada, lo que permite capturar su salida mediante una simple asignación$output = netdom .... La mayoría de las respuestas dadas a continuación implícitamente renuncianStart-Processa favor de la ejecución directa.-CredentialparámetroStart-Processes imprescindible, pero solo entonces (y si desea ejecutar un comando en una ventana separada). Y uno debe ser consciente de las limitaciones inevitables en ese caso: Sin capacidad de captura de salida, a menos que - no intercalada - texto en archivos , a través de-RedirectStandardOutputy-RedirectStandardError.Respuestas:
Has probado:
$OutputVariable = (Shell command) | Out-Stringfuente
Shell Commandcon lo que quiera ejecutar. Es así de simple.-iopción sin especificar un archivo de salida. Redirigir la salida usando2>&1como se describe en algunas de las otras respuestas es la solución.Nota: El comando en la pregunta utiliza
Start-Process, lo que impide la captura directa de la salida del programa de destino. En general, no lo useStart-Processpara ejecutar aplicaciones de consola de forma síncrona, solo invocalas directamente , como en cualquier shell. Al hacerlo, la aplicación se conecta a las transmisiones estándar de la consola de llamada, lo que permite capturar su salida mediante una simple asignación$output = netdom ..., como se detalla a continuación.Básicamente , la captura de resultados de utilidades externas funciona igual que con los comandos nativos de PowerShell (es posible que desee una actualización sobre cómo ejecutar herramientas externas ):
Tenga en cuenta que
$cmdOutputrecibe una matriz de objetos si<command>produce más de 1 objeto de salida , que en el caso de un programa externo significa una matriz de cadena que contiene las líneas de salida del programa .Si desea
$cmdOutputrecibir siempre una sola cadena , potencialmente de varias líneas , use$cmdOutput = <command> | Out-StringPara capturar la salida en una variable e imprimir en la pantalla :
O, si
<command>es un cmdlet o una función avanzada , puede usar el parámetro común-OutVariable/-ov:Tenga en cuenta que with
-OutVariable, a diferencia de los otros escenarios, siempre$cmdOutputes una colección , incluso si solo se emite un objeto. Específicamente, se devuelve una instancia del tipo de matriz . Vea este tema de GitHub para una discusión de esta discrepancia.[System.Collections.ArrayList]Para capturar la salida de múltiples comandos , use una subexpresión (
$(...)) o llame a un bloque de script ({ ... }) con&o.:Tenga en cuenta que la necesidad general de prefijar con
&(el operador de llamada) un comando individual cuyo nombre / ruta se cita , por ejemplo,$cmdOutput = & 'netdom.exe' ...no está relacionado con programas externos per se (se aplica igualmente a los scripts de PowerShell), pero es un requisito de sintaxis : PowerShell analiza una declaración que comienza con una cadena entrecomillada en modo de expresión por defecto, mientras que el modo de argumento es necesario para invocar comandos (cmdlets, programas externos, funciones, alias), que es lo que&garantiza.La diferencia clave entre
$(...)y& { ... }/. { ... }es que el primero recopila toda la entrada en la memoria antes de devolverlo como un todo, mientras que el segundo transmite la salida, adecuada para el procesamiento de canalización uno por uno.Las redirecciones también funcionan igual, fundamentalmente (pero vea las advertencias a continuación):
Sin embargo, para los comandos externos es más probable que lo siguiente funcione como se esperaba:
Consideraciones específicas para programas externos :
Los programas externos , debido a que operan fuera del sistema de tipos de PowerShell, solo devuelven cadenas a través de su flujo de éxito (stdout).
Si la salida contiene más de 1 línea , PowerShell la divide de manera predeterminada en una matriz de cadenas . Más exactamente, las líneas de salida se almacenan en una matriz de tipo
[System.Object[]]cuyos elementos son cadenas ([System.String]).Si desea que la salida sea una sola cadena potencialmente de varias líneas , canalice a
Out-String:$cmdOutput = <command> | Out-StringRedirigir stderr para stdout con el
2>&1fin de capturarlo también como parte de la secuencia de éxito, viene con advertencias :Para hacer
2>&1fusionar stdout y stderr en la fuente , permitacmd.exemanejar la redirección , utilizando los siguientes modismos:$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)$cmdOutput = cmd /c <command> '2>&1' | Out-String # single stringcmd /cinvocacmd.execon comando<command>y sale después de que<command>haya terminado.2>&1, lo que garantiza que se pase la redirección encmd.exelugar de que PowerShell la interprete.Tenga en cuenta que involucrar
cmd.exesignifica que sus reglas para escapar de los personajes y expandir las variables de entorno entran en juego, de forma predeterminada, además de los requisitos de PowerShell; en PS v3 + puede usar parámetros especiales--%(el llamado símbolo de stop-parsing ) para desactivar la interpretación de los parámetros restantes por parte de PowerShell, exceptocmd.exelas referencias de variables de entorno de estilo como%PATH%.Tenga en cuenta que dado que está combinando stdout y stderr en la fuente con este enfoque, no podrá distinguir entre líneas originadas por stdout y originadas por stderr en PowerShell; Si necesita esta distinción, use la propia
2>&1redirección de PowerShell ; consulte a continuación.Use la
2>&1redirección de PowerShell para saber qué líneas provienen de qué flujo :La salida de Stderr se captura como registros de error (
[System.Management.Automation.ErrorRecord]), no cadenas, por lo que la matriz de salida puede contener una mezcla de cadenas (cada cadena representa una línea estándar) y registros de error (cada registro representa una línea stderr) . Tenga en cuenta que, según lo solicitado por2>&1, tanto las cadenas como los registros de error se reciben a través del flujo de salida de éxito de PowerShell ).En la consola, los registros de error se imprimen en rojo , y el primero produce, por defecto , una visualización de varias líneas , en el mismo formato en que se mostraría el error sin terminación de un cmdlet; los registros de error posteriores también se imprimen en rojo, pero solo imprimen su mensaje de error , en una sola línea .
Cuando se envía a la consola , las cadenas suelen aparecer primero en la matriz de salida, seguidas de los registros de error (al menos entre un lote de salida de líneas stdout / stderr "al mismo tiempo"), pero, afortunadamente, cuando captura la salida , está correctamente intercalado , utilizando el mismo orden de salida que obtendría sin
2>&1; en otras palabras: cuando se envía a la consola , la salida capturada NO refleja el orden en el que el comando externo generó las líneas stdout y stderr.Si captura la salida completa en una sola cadena con
Out-String, PowerShell agregará líneas adicionales , porque la representación de cadena de un registro de error contiene información adicional, como ubicación (At line:...) y categoría (+ CategoryInfo ...); Curiosamente, esto solo se aplica al primer registro de error..ToString()método para cada objeto de salida en lugar de la tubería aOut-String:$cmdOutput = <command> 2>&1 | % { $_.ToString() };en PS v3 + puede simplificar a:
$cmdOutput = <command> 2>&1 | % ToString(Como beneficio adicional, si la salida no se captura, esto produce una salida correctamente entrelazada incluso cuando se imprime en la consola).
Alternativamente, filtrar los registros de error fuera y enviarlos a flujo de error de PowerShell con
Write-Error(como un bono, si la salida no se captura, lo que produce como salida correctamente intercalada, incluso cuando se imprime en la consola):fuente
<command>, no debe combinar el ejecutable y sus argumentos en una sola cadena; con la invocación a través decmd /custed puede hacerlo, y depende de la situación si tiene sentido o no. ¿A qué escenario se refiere y puede dar un ejemplo mínimo?+operador; lo siguiente también funciona:cmd /c c:\mycommand.exe $Args '2>&1'- PowerShell se encarga de pasar los elementos$Argscomo una cadena separada por espacios en ese caso, una característica llamada splatting .'2>&1'parte, y no encerrar en()tantos guiones tienden a hacer.Si desea redirigir también la salida del error, debe hacer lo siguiente:
O, si el nombre del programa tiene espacios:
fuente
O prueba esto. Capturará la salida en la variable $ scriptOutput:
fuente
$scriptOutput = & "netdom.exe" $paramsOtro ejemplo de la vida real:
Tenga en cuenta que este ejemplo incluye una ruta (que comienza con una variable de entorno). Tenga en cuenta que las comillas deben rodear la ruta y el archivo EXE, ¡pero no los parámetros!
Nota: No olvide el
&carácter delante del comando, pero fuera de las comillas.La salida de error también se recopila.
Me llevó un tiempo hacer funcionar esta combinación, así que pensé en compartirla.
fuente
Intenté las respuestas, pero en mi caso no obtuve la salida en bruto. En cambio, se convirtió en una excepción de PowerShell.
El resultado bruto que obtuve con:
fuente
Tengo lo siguiente para trabajar:
$ resultado te da lo necesario
fuente
Esto me funcionó:
fuente
Si todo lo que intenta hacer es capturar la salida de un comando, esto funcionará bien.
Lo uso para cambiar la hora del sistema, ya que
[timezoneinfo]::localsiempre produce la misma información, incluso después de haber realizado cambios en el sistema. Esta es la única forma en que puedo validar y registrar el cambio en la zona horaria:Lo que significa que tengo que abrir una nueva sesión de PowerShell para volver a cargar las variables del sistema.
fuente