Uwaga: Polecenie w pytaniu używa Start-Process, co zapobiega bezpośredniemu przechwytywaniu wyników programu docelowego. Generalnie nie używaj Start-Processdo 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 $cmdOutputotrzymuje 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 $cmdOutputzawsze 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, $cmdOutputjest 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>&1scalenia stdout i stderr u źródła , niech cmd.exeobsł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 /cwywołuje cmd.exepolecenie <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.exea nie interpretowane przez program PowerShell.
Zauważ, że angażowanie cmd.exeoznacza, ż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.exeodwoł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>&1przekierowania 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>&1zaró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-Processdo 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-Processz bezpośredniego wykonania.