Nota: viene utilizzato il comando nella domanda Start-Process
, che impedisce l'acquisizione diretta dell'output del programma di destinazione. Generalmente, non utilizzare Start-Process
per eseguire le applicazioni della console in modo sincrono, ma invocarle direttamente , come in qualsiasi shell. In questo modo l'applicazione viene connessa ai flussi standard della console di chiamata, consentendo di catturare l'output mediante una semplice assegnazione $output = netdom ...
, come descritto di seguito.
Fondamentalmente , catturare l'output da utility esterne funziona allo stesso modo dei comandi nativi di PowerShell (potresti voler avere un aggiornamento su come eseguire strumenti esterni ):
$cmdOutput = <command> # captures the command's success stream / stdout
Si noti che $cmdOutput
riceve una matrice di oggetti se <command>
produce più di 1 oggetto di output , che nel caso di un programma esterno significa una matrice di stringhe contenente le linee di output del programma .
Se si desidera $cmdOutput
ricevere sempre una stringa singola , potenzialmente multilinea , utilizzare
$cmdOutput = <command> | Out-String
Per acquisire l' output in una variabile e stamparlo sullo schermo :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Oppure, se si <command>
tratta di un cmdlet o di una funzione avanzata , è possibile utilizzare il parametro comune
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Si noti che con -OutVariable
, a differenza degli altri scenari, $cmdOutput
è sempre una raccolta , anche se viene emesso un solo oggetto. In particolare, [System.Collections.ArrayList]
viene restituita un'istanza del tipo di matrice .
Vedi questo numero di GitHub per una discussione di questa discrepanza.
Per acquisire l'output da più comandi , utilizzare una sottoespressione ( $(...)
) o chiamare un blocco di script ( { ... }
) con &
o .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Si noti che la necessità generale di aggiungere un prefisso &
(l'operatore di chiamata) a un singolo comando il cui nome / percorso è citato , ad esempio, $cmdOutput = & 'netdom.exe' ...
non è correlato ai programmi esterni di per sé (si applica ugualmente agli script di PowerShell), ma è un requisito di sintassi : PowerShell analizza un'istruzione che inizia con una stringa tra virgolette in modalità espressione per impostazione predefinita, mentre la modalità argomento è necessaria per invocare comandi (cmdlet, programmi esterni, funzioni, alias), che è ciò che &
garantisce.
La differenza chiave tra $(...)
e & { ... }
/ . { ... }
è che il primo raccoglie tutti gli input in memoria prima di restituirli nel loro insieme, mentre i secondi trasmettono l'output, adatto per l'elaborazione di una pipeline uno per uno.
Anche i reindirizzamenti funzionano allo stesso modo, fondamentalmente (ma vedi le avvertenze di seguito):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
Tuttavia, per i comandi esterni è più probabile che i seguenti funzionino come previsto:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Considerazioni specifiche per i programmi esterni :
I programmi esterni , poiché operano al di fuori del sistema di tipi di PowerShell, restituiscono sempre le stringhe solo attraverso il loro flusso di successo (stdout).
Se l'output contiene più di 1 riga , PowerShell per impostazione predefinita lo suddivide in una matrice di stringhe . Più precisamente, le righe di output sono memorizzate in una matrice di tipo i [System.Object[]]
cui elementi sono stringhe ( [System.String]
).
Se si desidera che l'output sia una stringa singola , potenzialmente multilinea , pipe aOut-String
:
$cmdOutput = <command> | Out-String
Il reindirizzamento di stderr su stdout2>&1
, in modo da catturarlo anche come parte del flusso di successo, viene fornito con avvertenze :
Per 2>&1
unire stdout e stderr all'origine , gestiamo il reindirizzamento , usando i seguenti modi di direcmd.exe
:
$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>
ed esce al <command>
termine.
- Nota le virgolette singole in giro
2>&1
, che assicurano che il reindirizzamento venga passato cmd.exe
anziché essere interpretato da PowerShell.
Si noti che coinvolgere cmd.exe
significa che entrano in gioco le sue regole per sfuggire ai caratteri e espandere le variabili di ambiente, per impostazione predefinita oltre ai requisiti di PowerShell; in PS v3 + è possibile utilizzare un parametro speciale --%
(il cosiddetto simbolo di arresto dell'analisi ) per disattivare l'interpretazione dei parametri rimanenti da PowerShell, ad eccezione di cmd.exe
riferimenti a variabili di ambiente in stile come %PATH%
.
Nota che dal momento che stai fondendo stdout e stderr alla fonte con questo approccio, non sarai in grado di distinguere tra le linee originate da stdout e originate da stderr in PowerShell; se hai bisogno di questa distinzione, usa il 2>&1
reindirizzamento di PowerShell, vedi sotto.
Usa il 2>&1
reindirizzamento di PowerShell per sapere quali linee provengono da quale stream :
L' output di Stderr viene acquisito come record di errore ( [System.Management.Automation.ErrorRecord]
), non stringhe, quindi l' array di output può contenere un mix di stringhe (ciascuna stringa che rappresenta una riga stdout) e record di errore (ogni record che rappresenta una riga stderr) . Si noti che, come richiesto da 2>&1
, sia le stringhe che i record di errore vengono ricevuti tramite il flusso di output di successo di PowerShell ).
Nella console, i record di errore vengono stampati in rosso e il primo per impostazione predefinita produce una visualizzazione a più righe , nello stesso formato che verrà visualizzato l'errore non terminante di un cmdlet; i successivi record di errore vengono stampati anche in rosso, ma stampano il loro messaggio di errore solo su una riga .
Quando si esegue l' output alla console , le stringhe in genere vengono prima nell'array di output, seguite dai record di errore (almeno tra un batch di righe stdout / stderr output "allo stesso tempo"), ma, fortunatamente, quando si acquisisce l'output , è opportunamente interfogliato , utilizzando lo stesso ordine di output senza cui si otterrebbe 2>&1
; in altre parole: quando si invia alla console , l'output catturato NON riflette l'ordine in cui le linee stdout e stderr sono state generate dal comando esterno.
Se si acquisisce l'intero output in una singola stringa conOut-String
, PowerShell aggiungerà ulteriori righe , poiché la rappresentazione in formato stringa di un record di errore contiene informazioni aggiuntive come location ( At line:...
) e category ( + CategoryInfo ...
); curiosamente, questo vale solo per il primo record di errore.
- Per aggirare questo problema, applicare il
.ToString()
metodo di ogni oggetto uscita invece di tubazioni a Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
in PS v3 + puoi semplificare a:
$cmdOutput = <command> 2>&1 | % ToString
(Come bonus, se l'output non viene catturato, questo produce output correttamente interfogliati anche quando si stampa sulla console.)
In alternativa, filtrare i record di errore fuori e li invia al flusso di errore di PowerShell conWrite-Error
(come bonus, se l'uscita non viene catturato, questo produce in uscita correttamente Interleaved anche quando si stampa alla console):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
per eseguire (per definizione esterne) applicazioni console in modo sincrono - basta invocarle direttamente , come in qualsiasi shell; vale a dire:netdom /verify $pc /domain:hosp.uhhg.org
. In questo modo l'applicazione viene connessa ai flussi standard della console chiamante, consentendo di catturare l'output mediante una semplice assegnazione$output = netdom ...
. La maggior parte delle risposte fornite di seguito implicitamente rinunciaStart-Process
a favore dell'esecuzione diretta.