Gostaria de executar um processo externo e capturar sua saída de comando para uma variável no PowerShell. Atualmente, estou usando isso:
$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait
Confirmei que o comando está em execução, mas preciso capturar a saída em uma variável. Isso significa que não posso usar o -RedirectOutput porque isso redireciona apenas para um arquivo.
powershell
process
Adam Bertram
fonte
fonte
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.-Credential
parâmetroStart-Process
é obrigatório - mas somente então (e se você quiser executar um comando em uma janela separada). E deve-se estar ciente das limitações inevitáveis nesse caso: Nenhuma capacidade de capturar saída, exceto como texto não intercalado nos arquivos , via-RedirectStandardOutput
e-RedirectStandardError
.Respostas:
Você tentou:
$OutputVariable = (Shell command) | Out-String
fonte
Shell Command
pelo que você deseja executar. É simples assim.-i
opção sem especificar um arquivo de saída. Redirecionar a saída usando2>&1
como descrito em algumas das outras respostas é a solução.Nota: O comando na pergunta usa
Start-Process
, o que impede a captura direta da saída do programa de destino. Geralmente, não useStart-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 ):
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 :
Ou, se
<command>
for um cmdlet ou uma função avançada , você pode usar o parâmetro comum-OutVariable
/-ov
: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.
: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):
No entanto, para comandos externos, é mais provável que o seguinte funcione conforme o esperado:
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 para
Out-String
:$cmdOutput = <command> | Out-String
O redirecionamento do stderr para o stdout
2>&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 , vamoscmd.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
chamacmd.exe
com o comando<command>
e sai após a<command>
conclusão.2>&1
, o que garante que o redirecionamento seja passado para, emcmd.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 paracmd.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 por2>&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 com
Out-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..ToString()
método para cada objeto em vez de tubulação de saídaOut-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 com
Write-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):fonte
<command>
, você não deve combinar o executável e seus argumentos em uma única sequência; com a invocação viacmd /c
você pode fazê-lo, e isso depende da situação, faça sentido ou não. A que cenário você está se referindo e você pode dar um exemplo mínimo?+
operador; o seguinte também funciona:cmd /c c:\mycommand.exe $Args '2>&1'
- O PowerShell se encarrega de passar os elementos$Args
como uma sequência separada por espaço, nesse caso, um recurso chamado splatting .'2>&1'
parte, e não está incluído em()
muitos roteiros.Se você deseja redirecionar também a saída de erro, é necessário:
Ou, se o nome do programa tiver espaços:
fonte
Ou tente isso. Ele capturará a saída na variável $ scriptOutput:
fonte
$scriptOutput = & "netdom.exe" $params
Outro exemplo da vida real:
Observe que este exemplo inclui um caminho (que começa com uma variável de ambiente). Observe que as aspas devem envolver o caminho e o arquivo EXE, mas não os parâmetros!
Nota: Não esqueça o
&
caractere na frente do comando, mas fora das aspas.A saída de erro também é coletada.
Demorei um pouco para que essa combinação funcionasse, então pensei em compartilhá-la.
fonte
Tentei as respostas, mas no meu caso não obtive a saída bruta. Em vez disso, foi convertido em uma exceção do PowerShell.
O resultado bruto que obtive com:
fonte
Eu tenho o seguinte para trabalhar:
O resultado $ fornece as informações necessárias
fonte
Essa coisa funcionou para mim:
fonte
Se tudo o que você está tentando fazer é capturar a saída de um comando, isso funcionará bem.
Eu o uso para alterar a hora do sistema, pois
[timezoneinfo]::local
sempre produz as mesmas informações, mesmo depois de você fazer alterações no sistema. Esta é a única maneira de validar e registrar a alteração no fuso horário:Significando que eu tenho que abrir uma nova sessão do PowerShell para recarregar as variáveis do sistema.
fonte