Uwaga: Polecenie w pytaniu używa Start-Process
, co zapobiega bezpośredniemu przechwytywaniu wyników programu docelowego. Generalnie nie używaj Start-Process
do synchronicznego wykonywania aplikacji konsolowych - po prostu wywołuj je bezpośrednio , jak w każdej powłoce. Dzięki temu aplikacja będzie połączona ze standardowymi strumieniami konsoli wywołującej, umożliwiając przechwytywanie jej danych wyjściowych przez proste przypisanie $output = netdom ...
, jak opisano szczegółowo poniżej.
Zasadniczo przechwytywanie danych wyjściowych z zewnętrznych narzędzi działa tak samo, jak w przypadku poleceń natywnych programu PowerShell (możesz chcieć odświeżyć informacje o wykonywaniu narzędzi zewnętrznych ):
$cmdOutput = <command> # captures the command's success stream / stdout
Zauważ, że $cmdOutput
otrzymuje tablicę obiektów, jeśli <command>
tworzy więcej niż 1 obiekt wyjściowy , co w przypadku programu zewnętrznego oznacza tablicę ciągów zawierającą wiersze wyjściowe programu .
Jeśli chcesz $cmdOutput
zawsze otrzymywać pojedynczy - potencjalnie wieloliniowy - ciąg , użyj
$cmdOutput = <command> | Out-String
Aby przechwycić wynik w zmiennej i wydrukować na ekranie :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Lub, jeśli <command>
jest to polecenie cmdlet lub funkcja zaawansowana , możesz użyć wspólnego parametru
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Zauważ, że z -OutVariable
, w przeciwieństwie do innych scenariuszy, $cmdOutput
jest zawsze kolekcja , nawet jeśli tylko jeden obiekt jest wyjście. W szczególności [System.Collections.ArrayList]
zwracane jest wystąpienie typu tablicowego .
Zobacz ten problem w serwisie GitHub, aby omówić tę rozbieżność.
Aby uchwycić dane wyjściowe z wielu poleceń , użyj subexpression ( $(...)
) lub wywołaj blok skryptu ( { ... }
) z &
lub .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Należy zauważyć, że ogólna potrzeba prefiksu z &
(operatora połączeń) pojedynczego polecenia, którego nazwa / ścieżka jest cytowany - na przykład $cmdOutput = & 'netdom.exe' ...
- nie jest związane z programami zewnętrznymi per se (to równie dotyczy skryptów PowerShell), ale jest składnia wymóg : PowerShell analizuje instrukcję, która domyślnie zaczyna się od ciągu znaków w cudzysłowie w trybie wyrażenia , podczas gdy tryb argumentów jest potrzebny do wywoływania poleceń (poleceń cmdlet, programów zewnętrznych, funkcji, aliasów), co zapewnia.&
Kluczowa różnica między $(...)
i & { ... }
/ . { ... }
polega na tym, że pierwszy z nich zbiera wszystkie dane wejściowe w pamięci przed zwróceniem go w całości, podczas gdy drugi przesyła dane wyjściowe, nadając się do przetwarzania potokowego jeden po drugim.
Przekierowania również działają zasadniczo tak samo (ale zobacz poniższe zastrzeżenia):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
Jednak w przypadku poleceń zewnętrznych bardziej prawdopodobne jest, że będą działać zgodnie z oczekiwaniami:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Uwagi szczególne dotyczące programów zewnętrznych :
Programy zewnętrzne , ponieważ działają poza systemem typów programu PowerShell, zwracają ciągi tylko za pośrednictwem ich strumienia sukcesu (stdout).
Jeśli dane wyjściowe zawierają więcej niż 1 wiersz , program PowerShell domyślnie dzieli je na tablicę ciągów . Dokładniej, wiersze wyjściowe są przechowywane w tablicy typu, [System.Object[]]
której elementami są stringi ( [System.String]
).
Jeśli chcesz, aby wyjście było pojedynczym , potencjalnie wieloliniowym ciągiem , potokuj doOut-String
:
$cmdOutput = <command> | Out-String
Przekierowanie stderr na stdout za pomocą2>&1
, aby również uchwycić go jako część strumienia sukcesu, wiąże się z pewnymi zastrzeżeniami :
Aby dokonać 2>&1
scalenia stdout i stderr u źródła , niech cmd.exe
obsłużyć przekierowanie , stosując następujące idiomy:
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string
cmd /c
wywołuje cmd.exe
polecenie <command>
i kończy działanie po <command>
zakończeniu.
- Zwróć uwagę na pojedyncze cudzysłowy
2>&1
, które zapewniają, że przekierowanie jest przekazywane, cmd.exe
a nie interpretowane przez program PowerShell.
Zauważ, że angażowanie cmd.exe
oznacza, że jego reguły ucieczki znaków i rozszerzania zmiennych środowiskowych wchodzą w grę, domyślnie oprócz własnych wymagań PowerShell; w PS v3 + można użyć specjalnego parametru --%
(tak zwanego symbolu stop-parsing ), aby wyłączyć interpretację pozostałych parametrów przez PowerShell, z wyjątkiem cmd.exe
odwołań do zmiennych środowiskowych w stylu, takich jak %PATH%
.
Zauważ, że ponieważ łączysz stdout i stderr w źródle za pomocą tego podejścia, nie będziesz w stanie rozróżnić linii pochodzących ze stdout i stderr w PowerShell; jeśli potrzebujesz tego rozróżnienia, użyj własnego 2>&1
przekierowania PowerShell - patrz poniżej.
Użyj przekierowania PowerShell, 2>&1
aby dowiedzieć się, które linie pochodzą z jakiego strumienia :
Stderr wyjście jest ujęte jako zapisy o błędach ( [System.Management.Automation.ErrorRecord]
) nie strun, więc tablica wyjściowy może zawierać mieszankę z ciągów (ciąg reprezentujący każdą linię wyjścia) i zapisy o błędach (każdy rekord reprezentująca linię stderr) . Należy zauważyć, że zgodnie z żądaniem 2>&1
zarówno ciągi, jak i rekordy błędów są odbierane przez strumień wyjściowy sukcesu programu PowerShell ).
W konsoli rekordy błędów są drukowane na czerwono , a pierwszy z nich domyślnie wyświetla wiele wierszy w tym samym formacie, w jakim byłby wyświetlany niekończący błąd polecenia cmdlet; Kolejne rekordy błąd drukowania w kolorze czerwonym, a także, ale tylko wydrukować swój błąd wiadomość , na jednej linii .
Podczas wysyłania do konsoli ciągi zwykle znajdują się na pierwszym miejscu w tablicy wyjściowej, a po nich następują rekordy błędów (przynajmniej wśród partii linii stdout / stderr wyświetlanych „w tym samym czasie”), ale na szczęście podczas przechwytywania danych wyjściowych , jest odpowiednio przepleciony , używając tej samej kolejności wyjściowej, jaką można uzyskać bez 2>&1
; innymi słowy: podczas wysyłania do konsoli przechwycone dane wyjściowe NIE odzwierciedlają kolejności, w jakiej linie stdout i stderr zostały wygenerowane przez polecenie zewnętrzne.
Jeśli przechwycisz całe dane wyjściowe w jednym ciągu za pomocąOut-String
, PowerShell doda dodatkowe wiersze , ponieważ reprezentacja ciągu rekordu błędu zawiera dodatkowe informacje, takie jak location ( At line:...
) i category ( + CategoryInfo ...
); co ciekawe, dotyczy to tylko pierwszego rekordu błędu.
- Aby obejść ten problem, należy zastosować
.ToString()
metodę dla każdego obiektu wyjściowego zamiast rurociągów do Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
w PS v3 + możesz uprościć, aby:
$cmdOutput = <command> 2>&1 | % ToString
(Jako bonus, jeśli wyjście nie jest przechwytywane, daje to poprawnie przepleciony wynik nawet podczas drukowania na konsolę).
Alternatywnie, filtrować rekordy błędach out i wysyłać je do strumienia błędów PowerShell jest zWrite-Error
(jako bonus, jeśli wyjście nie jest zrobione, to produkuje wyjście prawidłowo przeplotem nawet podczas drukowania do konsoli):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
do synchronicznego wykonywania (z definicji zewnętrznych) aplikacji konsolowych - po prostu wywołaj je bezpośrednio , jak w każdej powłoce; a mianowicie:netdom /verify $pc /domain:hosp.uhhg.org
. Dzięki temu aplikacja będzie połączona ze standardowymi strumieniami konsoli wywołującej, umożliwiając przechwytywanie jej danych wyjściowych przez proste przypisanie$output = netdom ...
. Większość odpowiedzi udzielonych poniżej domyślnie rezygnujeStart-Process
z bezpośredniego wykonania.