Nota: O comando na pergunta usa Start-Process
, o que impede a captura direta da saída do programa de destino. Geralmente, não use Start-Process
para executar aplicativos de console de forma síncrona - apenas invoque-os diretamente , como em qualquer shell. Isso mantém o aplicativo conectado aos fluxos padrão do console de chamada, permitindo que sua saída seja capturada por atribuição simples $output = netdom ...
, conforme detalhado abaixo.
Fundamentalmente , a captura de saída de utilitários externos funciona da mesma maneira que com os comandos nativos do PowerShell (você pode querer se atualizar sobre como executar ferramentas externas ):
$cmdOutput = <command> # captures the command's success stream / stdout
Observe que $cmdOutput
recebe uma matriz de objetos se <command>
produz mais de 1 objeto de saída , o que, no caso de um programa externo, significa uma matriz de cadeias contendo as linhas de saída do programa .
Se você deseja $cmdOutput
sempre receber uma única sequência - potencialmente com várias linhas - , use
$cmdOutput = <command> | Out-String
Para capturar a saída em uma variável e imprimir na tela :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Ou, se <command>
for um cmdlet ou uma função avançada , você pode usar o parâmetro comum
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Observe que -OutVariable
, ao contrário dos outros cenários, sempre$cmdOutput
é uma coleção , mesmo que apenas um objeto seja produzido. Especificamente, uma instância do tipo de matriz é retornada.
Veja esta edição do GitHub para uma discussão sobre essa discrepância.[System.Collections.ArrayList]
Para capturar a saída de vários comandos , use uma subexpressão ( $(...)
) ou chame um bloco de script ( { ... }
) com &
ou .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Observe que a necessidade geral de prefixar com &
(o operador de chamada) um comando individual cujo nome / caminho é citado - por exemplo, $cmdOutput = & 'netdom.exe' ...
- não está relacionado a programas externos per se (ele se aplica igualmente aos scripts do PowerShell), mas é um requisito de sintaxe : PowerShell analisa uma instrução que começa com uma string entre aspas no modo de expressão por padrão, enquanto o modo de argumento é necessário para chamar comandos (cmdlets, programas externos, funções, aliases), o que &
garante.
A principal diferença entre $(...)
e & { ... }
/ . { ... }
é que o primeiro coleta toda a entrada na memória antes de devolvê-lo como um todo, enquanto o último transmite a saída, adequado para o processamento de pipeline um por um.
Os redirecionamentos também funcionam da mesma maneira, fundamentalmente (mas veja as advertências abaixo):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
No entanto, para comandos externos, é mais provável que o seguinte funcione conforme o esperado:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Considerações específicas para programas externos :
Programas externos , porque operam fora do sistema de tipos do PowerShell, sempre retornam seqüências de caracteres por meio do fluxo de sucesso (stdout).
Se a saída contiver mais de 1 linha , o PowerShell, por padrão, a dividirá em uma matriz de seqüências de caracteres . Mais precisamente, as linhas de saída são armazenadas em uma matriz do tipo [System.Object[]]
cujos elementos são cadeias de caracteres ( [System.String]
).
Se você deseja que a saída seja uma única sequência potencialmente com várias linhas , canalize paraOut-String
:
$cmdOutput = <command> | Out-String
O redirecionamento do stderr para o stdout2>&1
, para capturá-lo também como parte do fluxo de sucesso, vem com advertências :
Para fazer 2>&1
stdout merge e stderr na fonte , vamos cmd.exe
lidar com o redirecionamento , usando as seguintes expressões:
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string
cmd /c
chama cmd.exe
com o comando <command>
e sai após a <command>
conclusão.
- Observe as aspas simples
2>&1
, o que garante que o redirecionamento seja passado para, em cmd.exe
vez de ser interpretado pelo PowerShell.
Observe que envolver cmd.exe
significa que suas regras para escapar caracteres e expandir variáveis de ambiente entram em jogo, por padrão, além dos próprios requisitos do PowerShell; no PS v3 +, você pode usar um parâmetro especial --%
(o chamado símbolo de análise de parada ) para desativar a interpretação dos parâmetros restantes pelo PowerShell, exceto para cmd.exe
referências de variáveis de ambiente no estilo, como %PATH%
.
Observe que, como você está mesclando stdout e stderr na fonte com essa abordagem, não será possível distinguir entre linhas originadas por stdout e originadas por stderr no PowerShell; se você precisar dessa distinção, use o próprio 2>&1
redirecionamento do PowerShell - veja abaixo.
Use o 2>&1
redirecionamento do PowerShell para saber quais linhas vieram de qual fluxo :
A saída Stderr é capturada como registros de erro ( [System.Management.Automation.ErrorRecord]
), não cadeias, portanto a matriz de saída pode conter uma mistura de cadeias (cada cadeia representando uma linha stdout) e registros de erro (cada registro representando uma linha stderr) . Observe que, conforme solicitado por 2>&1
, as seqüências de caracteres e os registros de erro são recebidos pelo fluxo de saída de sucesso do PowerShell ).
No console, os registros de erro são impressos em vermelho , e o primeiro, por padrão, produz exibição em várias linhas , no mesmo formato que o erro sem fim de um cmdlet seria exibido; os registros de erro subsequentes também são impressos em vermelho, mas apenas imprimem sua mensagem de erro em uma única linha .
Ao enviar para o console , as sequências geralmente vêm primeiro na matriz de saída, seguidas pelos registros de erro (pelo menos entre um lote de linhas stdout / stderr emitidas "ao mesmo tempo"), mas, felizmente, quando você captura a saída , é intercalado adequadamente , usando a mesma ordem de saída que você obteria sem 2>&1
; em outras palavras: ao enviar para o console , a saída capturada NÃO reflete a ordem na qual as linhas stdout e stderr foram geradas pelo comando externo.
Se você capturar toda a saída em uma única string comOut-String
, o PowerShell adicionará linhas extras , porque a representação da string de um registro de erro contém informações adicionais, como local ( At line:...
) e categoria ( + CategoryInfo ...
); curiosamente, isso se aplica apenas ao primeiro registro de erro.
- Para contornar esse problema, aplicar o
.ToString()
método para cada objeto em vez de tubulação de saída Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
no PS v3 +, você pode simplificar:
$cmdOutput = <command> 2>&1 | % ToString
(Como bônus, se a saída não for capturada, isso produzirá uma saída adequadamente intercalada, mesmo ao imprimir no console).
Alternativamente, filtrar os registros de erro fora e enviá-los para fluxo de erro do PowerShell comWrite-Error
(como um bônus, se a saída não é capturado, este produz uma saída propriamente intercalado mesmo quando a impressão para o console):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
para executar aplicativos de console (por definição externos) de forma síncrona - apenas invoque-os diretamente , como em qualquer shell; a saber:netdom /verify $pc /domain:hosp.uhhg.org
. Isso mantém o aplicativo conectado aos fluxos padrão do console de chamada, permitindo que sua saída seja capturada por atribuição simples$output = netdom ...
. A maioria das respostas dadas abaixo renuncia implicitamente aStart-Process
favor da execução direta.