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 -Wait
He 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-Process
para 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-Process
a favor de la ejecución directa.-Credential
parámetroStart-Process
es 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-RedirectStandardOutput
y-RedirectStandardError
.Respuestas:
Has probado:
$OutputVariable = (Shell command) | Out-String
fuente
Shell Command
con lo que quiera ejecutar. Es así de simple.-i
opción sin especificar un archivo de salida. Redirigir la salida usando2>&1
como 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-Process
para 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
$cmdOutput
recibe 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
$cmdOutput
recibir siempre una sola cadena , potencialmente de varias líneas , use$cmdOutput = <command> | Out-String
Para 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$cmdOutput
es 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-String
Redirigir stderr para stdout con el
2>&1
fin de capturarlo también como parte de la secuencia de éxito, viene con advertencias :Para hacer
2>&1
fusionar stdout y stderr en la fuente , permitacmd.exe
manejar 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 string
cmd /c
invocacmd.exe
con comando<command>
y sale después de que<command>
haya terminado.2>&1
, lo que garantiza que se pase la redirección encmd.exe
lugar de que PowerShell la interprete.Tenga en cuenta que involucrar
cmd.exe
significa 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.exe
las 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>&1
redirección de PowerShell ; consulte a continuación.Use la
2>&1
redirecció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 /c
usted 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$Args
como 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" $params
Otro 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]::local
siempre 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