Nota: viene utilizzato il comando nella domanda Start-Process, che impedisce l'acquisizione diretta dell'output del programma di destinazione. Generalmente, non utilizzare Start-Processper 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 $cmdOutputriceve 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 $cmdOutputricevere 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>&1unire 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 /cinvoca cmd.execon comando <command>ed esce al <command>termine.
- Nota le virgolette singole in giro
2>&1, che assicurano che il reindirizzamento venga passato cmd.exeanziché essere interpretato da PowerShell.
Si noti che coinvolgere cmd.exesignifica 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.exeriferimenti 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>&1reindirizzamento 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-Processper 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-Processa favore dell'esecuzione diretta.