Chciałbym uruchomić proces zewnętrzny i przechwycić jego dane wyjściowe polecenia do zmiennej w programie PowerShell. Obecnie używam tego:
$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait
Potwierdziłem, że polecenie jest wykonywane, ale muszę przechwycić dane wyjściowe do zmiennej. Oznacza to, że nie mogę użyć -RedirectOutput, ponieważ to tylko przekierowuje do pliku.
powershell
process
Adam Bertram
źródło
źródło
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.-Credential
parametruStart-Process
jest koniecznością - ale tylko wtedy (i jeśli chcesz uruchomić polecenie w osobnym oknie). I należy być świadomym nieuniknionych ograniczeń w tym przypadku: Brak możliwości przechwytywania danych wyjściowych, z wyjątkiem - bez przeplotu - tekstu w plikach , za pośrednictwem-RedirectStandardOutput
i-RedirectStandardError
.Odpowiedzi:
Czy próbowałeś:
$OutputVariable = (Shell command) | Out-String
źródło
Shell Command
czymkolwiek, co chcesz uruchomić. To takie proste.-i
opcji bez określania pliku wyjściowego.2>&1
Rozwiązaniem jest przekierowanie danych wyjściowych przy użyciu sposobu opisanego w niektórych innych odpowiedziach.Uwaga: Polecenie w pytaniu używa
Start-Process
, co zapobiega bezpośredniemu przechwytywaniu wyników programu docelowego. Generalnie nie używajStart-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 ):
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 :
Lub, jeśli
<command>
jest to polecenie cmdlet lub funkcja zaawansowana , możesz użyć wspólnego parametru-OutVariable
/-ov
: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.
: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):
Jednak w przypadku poleceń zewnętrznych bardziej prawdopodobne jest, że będą działać zgodnie z oczekiwaniami:
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 do
Out-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 , niechcmd.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łujecmd.exe
polecenie<command>
i kończy działanie po<command>
zakończeniu.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ątkiemcmd.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 żądaniem2>&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..ToString()
metodę dla każdego obiektu wyjściowego zamiast rurociągów doOut-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 z
Write-Error
(jako bonus, jeśli wyjście nie jest zrobione, to produkuje wyjście prawidłowo przeplotem nawet podczas drukowania do konsoli):źródło
<command>
, nie wolno łączyć pliku wykonywalnego i jego argumentów w jeden ciąg; z przywołaniem przezcmd /c
ciebie możesz to zrobić i zależy to od sytuacji, czy ma to sens, czy nie. Do którego scenariusza się odnosisz i czy możesz podać minimalny przykład?+
operatorem; działa również to, co następuje:cmd /c c:\mycommand.exe $Args '2>&1'
- PowerShell dba o przekazanie elementów$Args
jako ciągu oddzielonego spacjami w tym przypadku, funkcja zwana splatting .'2>&1'
część, a nie zamknięcie się w()
tak wielu skryptach.Jeśli chcesz również przekierować wyjście błędu, musisz zrobić:
Lub, jeśli nazwa programu zawiera spacje:
źródło
Albo spróbuj tego. Przechwytuje dane wyjściowe do zmiennej $ scriptOutput:
źródło
$scriptOutput = & "netdom.exe" $params
Inny przykład z życia wzięty:
Zwróć uwagę, że ten przykład zawiera ścieżkę (która zaczyna się od zmiennej środowiskowej). Zauważ, że cudzysłowy muszą otaczać ścieżkę i plik EXE, ale nie parametry!
Uwaga: nie zapomnij
&
znaku przed poleceniem, ale poza cudzysłowami.Zbierane są również informacje o błędach.
Zajęło mi trochę czasu, zanim ta kombinacja zadziałała, więc pomyślałem, że podzielę się nią.
źródło
Wypróbowałem odpowiedzi, ale w moim przypadku nie otrzymałem surowego wyniku. Zamiast tego został przekonwertowany na wyjątek programu PowerShell.
Surowy wynik, który otrzymałem:
źródło
Do pracy dostałem:
$ wynik daje ci to, co potrzebne
źródło
Ta rzecz zadziałała dla mnie:
źródło
Jeśli wszystko, co próbujesz zrobić, to przechwycić dane wyjściowe z polecenia, to zadziała dobrze.
Używam go do zmiany czasu systemowego, ponieważ
[timezoneinfo]::local
zawsze produkuje te same informacje, nawet po wprowadzeniu zmian w systemie. Tylko w ten sposób mogę zweryfikować i zarejestrować zmianę strefy czasowej:Oznacza to, że muszę otworzyć nową sesję PowerShell, aby ponownie załadować zmienne systemowe.
źródło