Jak powiedzieć PowerShellowi, aby czekał na zakończenie każdego polecenia przed rozpoczęciem następnego?

212

Mam skrypt programu PowerShell 1.0, aby po prostu otworzyć kilka aplikacji. Pierwsza to maszyna wirtualna, a pozostałe to aplikacje programistyczne. Chcę, aby maszyna wirtualna zakończyła uruchamianie przed otwarciem pozostałych aplikacji.

W bashu mógłbym tylko powiedzieć "cmd1 && cmd2"

To właśnie mam ...

C:\Applications\VirtualBox\vboxmanage startvm superdooper
    &"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
John Mee
źródło

Odpowiedzi:

367

Zwykle w przypadku poleceń wewnętrznych PowerShell czeka przed uruchomieniem następnego polecenia. Jedynym wyjątkiem od tej reguły jest EXE oparty na zewnętrznym podsystemie Windows. Pierwsza sztuczka polega na tym, aby Out-Nulltak:

Notepad.exe | Out-Null

PowerShell poczeka, aż proces Notepad.exe zostanie zakończony, zanim przejdzie dalej. To fajne, ale subtelne do odczytania po przeczytaniu kodu. Możesz także użyć Start-Process z parametrem -Wait:

Start-Process <path to exe> -NoNewWindow -Wait

Jeśli używasz wersji rozszerzeń społeczności PowerShell, jest to:

$proc = Start-Process <path to exe> -NoNewWindow -PassThru
$proc.WaitForExit()

Inną opcją w PowerShell 2.0 jest użycie zadania w tle:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Keith Hill
źródło
2
Mój błąd. Rozpocznij proces - Zaczekaj działa świetnie, ale teraz widzę, że nie tego szukałem ... Właściwie to próbuję poczekać, aż VM zakończy ładowanie. Wyobrażam sobie, że to będzie trudne. Przypuszczam, że będę musiał znaleźć dla tego nowy wątek. Dzięki, ale.
John Mee,
7
Genialne, | out-nullzrobiłem tylko to, czego potrzebowałem. Próbowałem używać, Start-Jobale ponieważ przekazuję wyniki funkcji jako parametry, mam na sobie trochę skitzoidu, więc nie mogłem użyć ostatniej sugestii ...
jcolebrand 11.09.12
6
Na marginesie, jeśli musisz przekazać wiele argumentów -ArgumentList, oddziel je przecinkami, takimi jak -ArgumentList /D=test,/S.
sschuberth
1
Dziękujemy za proste rozwiązanie „<ścieżka do exe> | Out-Null”! Problem z metodą „Start-Process <ścieżka do exe> -NoNewWindow -Wait” polega na tym, że PowerShell zatrzymuje się, aż wszystkie procesy potomne odrodzone przez rodzica zostaną zakończone, nawet jeśli rodzic zakończy przed nimi. Spowodowało to problemy z naszym programem instalacyjnym.
zax
To jest to, czego używam, aby czekać na uruchomienie maszyny wirtualnej Start-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName while ((Get-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName -Status | `select -ExpandProperty Statusy |`? {$ _.Code -match "PowerState"} | `select -ExpandProperty DisplayStatus) -ne" VM running ") {Start-Sleep -s 2} Start-Sleep -s 5 ## Daj maszynie wirtualnej czas na pojawienie się, aby mogła zaakceptować zdalne żądania
andrewmo
47

Oprócz użycia Start-Process -Wait, potokowanie danych wyjściowych pliku wykonywalnego spowoduje, że Powershell zaczeka. W zależności od potrzeby, będę zazwyczaj rura do Out-Null, Out-Default, Out-Stringlub Out-String -Stream. Oto długa lista niektórych innych opcji wyjściowych.

# Saving output as a string to a variable.
$output = ping.exe example.com | Out-String

# Filtering the output.
ping stackoverflow.com | where { $_ -match '^reply' }

# Using Start-Process affords the most control.
Start-Process -Wait SomeExecutable.com

Tęsknię za operatorami stylu CMD / Bash, o których wspominałeś (&, &&, ||). Wydaje się, że musimy być bardziej gadatliwi w Powershell .

Nathan Hartley
źródło
Jest to niewiarygodnie dobre rozwiązanie, jeśli chcesz przeanalizować dane wyjściowe!
Jan Rothkegel,
Wskazanie listy przekierowujących Out-xxxx szybko pozwoliło mi zrozumieć, dlaczego powinienem używać Out-Default zamiast Out-Null , ponieważ musiałem zachować dane wyjściowe konsoli.
Julio Nobre
Zauważ, że nie jest wymagana dodatkowa praca, aby synchronicznie uruchamiać aplikacje konsolowe - jak w każdej powłoce, jest to zachowanie domyślne. Piping do Out-String zmienia dane wyjściowe na pojedynczy, wielowierszowy ciąg, podczas gdy PowerShell domyślnie zwraca tablicę wierszy . Start-Processnależy unikać aplikacji konsolowych (chyba że naprawdę chcesz uruchomić je w nowym oknie ), ponieważ nie będziesz w stanie przechwycić ani przekierować ich danych wyjściowych.
mklement0
W rzeczywistości to, czy natywne polecenie działa synchronicznie (z wyjściem), czy asynchronicznie, zależy od pliku wykonywalnego. Jako przykład, bez przechwytywania danych wyjściowych, następujące tylko wyniki „bingo”. & „C: \ Program Files \ Mozilla Firefox \ firefox.exe” -? ; „bingo”
Nathan Hartley,
12

Wystarczy użyć „Proces oczekiwania”:

"notepad","calc","wmplayer" | ForEach-Object {Start-Process $_} | Wait-Process ;dir

Praca jest wykonywana

Flaviofire
źródło
8

Jeśli użyjesz Start-Process <path to exe> -NoNewWindow -Wait

Możesz także użyć tej -PassThruopcji, aby wygenerować echo.

Sandy Chapman
źródło
Zauważ, że -PassThrunie echo danych wyjściowych (aplikacja nie będąca konsolą z definicji nie generuje danych wyjściowych konsoli), generuje System.Diagnostics.Processinstancję, która reprezentuje nowo uruchomiony proces, więc możesz sprawdzić jego właściwości i poczekać, aż wyjdzie później.
mklement0
6

Niektóre programy nie potrafią bardzo dobrze przetwarzać strumienia wyjściowego, użycie potoku Out-Nullmoże go nie zablokować.
I Start-Processpotrzebuje -ArgumentListprzełącznika do przekazywania argumentów, co nie jest tak wygodne.
Istnieje również inne podejście.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
ifree
źródło
1
jak działa wiele argumentów w tym wywołaniu? jak są one rozdzielone? jak radzić sobie z ucieczką różnych zagnieżdżonych łańcuchów? ty!
AnneTheAgile,
1
@AnneTheAgile doc używa spacji do oddzielenia argumentów, dla
znaków
ty @ifree, dostałem testcomplete do uruchomienia w ten sposób! Użyłem pojedynczych cudzysłowów wokół listy argumentów rozdzielanych spacjami. [1]; ale teraz echo $ kod zakończenia = fałsz, który nie był moim kodem powrotu z procesu? [1] $ exitCode = [Diagnostics.Process] :: Start („c: \ Program Files (x86) \ SmartBear \ TestComplete 10 \ Bin \ TestComplete.exe”, „” c: \ Users \ ME \ Documents \ TestComplete 10 Projekty \ hig4TestProject1 \ hig4TestProject1.pjs "/ run / project: myProj / test:" KeywordTests | zTdd1_Good "/ exit ') .WaitForExit (60)
AnneTheAgile
3

Dołączenie opcji -NoNewWindowpowoduje błąd:Start-Process : This command cannot be executed due to the error: Access is denied.

Jedynym sposobem, w jaki mogłem go uruchomić, było zadzwonienie:

Start-Process <path to exe> -Wait
twasbrillig
źródło
0

Idąc dalej, możesz nawet parsować w locie

na przykład

& "my.exe" | %{
    if ($_ -match 'OK')
    { Write-Host $_ -f Green }
    else if ($_ -match 'FAIL|ERROR')
    { Write-Host $_ -f Red }
    else 
    { Write-Host $_ }
}
Justin
źródło
0

Zawsze jest cmd. Może być mniej irytujące, jeśli masz problemy z cytowaniem argumentów do rozpoczęcia procesu:

cmd /c start /wait notepad
js2010
źródło