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 use Start-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 ):
$cmdOutput = <command> # captures the command's success stream / stdout
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 :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
O, si <command>
es un cmdlet o una función avanzada , puede usar el parámetro común
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
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 .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
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):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
Sin embargo, para los comandos externos es más probable que lo siguiente funcione como se esperaba:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
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 aOut-String
:
$cmdOutput = <command> | Out-String
Redirigir stderr para stdout con el2>&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 , permita cmd.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
invoca cmd.exe
con comando <command>
y sale después de que <command>
haya terminado.
- Tenga en cuenta las comillas simples
2>&1
, lo que garantiza que se pase la redirección en cmd.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, excepto cmd.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 por 2>&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 conOut-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.
- Para evitar este problema, aplique el
.ToString()
método para cada objeto de salida en lugar de la tubería a Out-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 conWrite-Error
(como un bono, si la salida no se captura, lo que produce como salida correctamente intercalada, incluso cuando se imprime en la consola):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
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.