Wie kann man PowerShell anweisen, auf das Beenden jedes Befehls zu warten, bevor der nächste gestartet wird?

211

Ich habe ein PowerShell 1.0-Skript, um nur eine Reihe von Anwendungen zu öffnen. Die erste ist eine virtuelle Maschine und die anderen sind Entwicklungsanwendungen. Ich möchte, dass die virtuelle Maschine den Startvorgang beendet, bevor die restlichen Anwendungen geöffnet werden.

In Bash könnte ich nur sagen "cmd1 && cmd2"

Das habe ich ...

C:\Applications\VirtualBox\vboxmanage startvm superdooper
    &"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
John Mee
quelle

Antworten:

366

Normalerweise wartet PowerShell bei internen Befehlen, bevor der nächste Befehl gestartet wird. Eine Ausnahme von dieser Regel ist die auf externem Windows-Subsystem basierende EXE-Datei. Der erste Trick besteht darin, die Pipeline so zu Out-Nullmögen:

Notepad.exe | Out-Null

PowerShell wartet, bis der Prozess Notepad.exe beendet wurde, bevor es fortgesetzt wird. Das ist geschickt, aber etwas subtil, wenn man den Code liest. Sie können Start-Process auch mit dem Parameter -Wait verwenden:

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

Wenn Sie die PowerShell Community Extensions-Version verwenden, gilt Folgendes:

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

Eine weitere Option in PowerShell 2.0 ist die Verwendung eines Hintergrundjobs:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Keith Hill
quelle
2
Mein Fehler. Start-Prozess -Warten funktioniert gut, aber jetzt sehe ich, dass dies nicht das ist, wonach ich gesucht habe ... Ich versuche tatsächlich zu warten, bis die VM fertig ist, um das Booten zu beenden. Ich kann mir vorstellen, dass das schwierig wird. Ich nehme an, ich muss dafür einen neuen Thread finden. Danke aber.
John Mee
7
Genial, | out-nulltat genau das, was ich brauchte. Versucht mit, Start-Jobaber weil ich die Ergebnisse von Funktionen als Parameter übergebe, wurde es ein wenig skitzoid auf mich, so dass ich den letzten Vorschlag nicht verwenden konnte ...
jcolebrand
6
Nebenbei bemerkt, wenn Sie mehrere Argumente mit übergeben müssen -ArgumentList, trennen Sie sie durch Kommas wie -ArgumentList /D=test,/S.
Sschuberth
1
Vielen Dank für die einfache Lösung "<path to exe> | Out-Null"! Das Problem bei der Methode "Start-Process <Pfad zu exe> -NoNewWindow -Wait" besteht darin, dass die PowerShell angehalten wird, bis alle vom übergeordneten Prozess erzeugten untergeordneten Prozesse abgeschlossen sind, auch wenn der übergeordnete Prozess vor ihnen beendet wird. Dies verursachte Probleme mit unserem Setup-Programm.
Zax
Auf diese Weise warte ich, bis eine VM gestartet ist. Start-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName while ((Get-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName -Status | `select -ExpandProperty Status |)? _.Code -match "PowerState"} | `select -ExpandProperty DisplayStatus) -ne" VM wird ausgeführt ") {Start-Sleep -s 2} Start-Sleep -s 5 ## Geben Sie der VM Zeit zum Hochfahren, damit sie akzeptieren kann Remote-Anfragen
andrewmo
47

Neben der Verwendung Start-Process -Waitlässt das Weiterleiten der Ausgabe einer ausführbaren Datei Powershell warten. Je nach Bedarf, werde ich normalerweise Rohr Out-Null, Out-Default, Out-Stringoder Out-String -Stream. Hier ist eine lange Liste einiger anderer Ausgabeoptionen.

# 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

Ich vermisse die CMD / Bash-Operatoren, auf die Sie verwiesen haben (&, &&, ||). Es scheint, dass wir mit Powershell ausführlicher umgehen müssen .

Nathan Hartley
quelle
Dies ist eine unglaublich gute Lösung, wenn Sie die Ausgabe analysieren müssen!
Jan Rothkegel
Durch das Hinweisen auf die Out-xxxx- Redirector-Liste konnte ich schnell verstehen, warum ich Out-Default anstelle von Out-Null verwenden sollte , da ich die Konsolenausgabe beibehalten musste.
Julio Nobre
Beachten Sie, dass keine zusätzliche Arbeit erforderlich ist, um Konsolenanwendungen synchron auszuführen - wie in jeder Shell ist dies das Standardverhalten. Durch Piping Out-String wird die Ausgabe in eine einzelne mehrzeilige Zeichenfolge geändert , während PowerShell standardmäßig ein Array von Zeilen zurückgibt . Start-Processsollte für Konsolenanwendungen vermieden werden (es sei denn, Sie möchten sie wirklich in einem neuen Fenster ausführen ), da Sie ihre Ausgabe nicht erfassen oder umleiten können.
mklement0
Ob ein nativer Befehl synchron (mit Ausgabe) oder asynchron ausgeführt wird, hängt von der ausführbaren Datei ab. Ohne die Ausgabe zu erfassen, wird im Folgenden nur "Bingo" ausgegeben. & 'C: \ Programme \ Mozilla Firefox \ Firefox.exe' -? ;; 'Bingo'
Nathan Hartley
12

Verwenden Sie einfach "Wait-process":

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

Arbeit ist erledigt

Flaviofire
quelle
8

Wenn du benutzt Start-Process <path to exe> -NoNewWindow -Wait

Sie können die -PassThruOption auch verwenden, um die Ausgabe zu wiederholen.

Sandy Chapman
quelle
Beachten Sie, dass -PassThrunicht Echo Ausgang (eine nicht-Konsole-Anwendung per Definition nicht Konsolenausgabe erzeugen), es gibt eine System.Diagnostics.ProcessInstanz, die den neu gestarteten Prozess darstellt, so dass Sie seine Eigenschaften und Warte untersuchen können es später zu verlassen.
mklement0
6

Einige Programme können den Ausgabestream nicht sehr gut verarbeiten. Wenn Sie Pipe verwenden, wird er Out-Nullmöglicherweise nicht blockiert.
Und Start-Processbraucht den -ArgumentListSchalter, um Argumente zu übergeben, nicht so bequem.
Es gibt auch einen anderen Ansatz.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
ich befreie
quelle
1
Wie funktionieren mehrere Argumente in diesem Aufruf? Wie werden sie abgegrenzt? Wie geht man mit verschiedenen verschachtelten Zeichenfolgen um? ty!
AnneTheAgile
1
@ AnneTheAgile doc verwenden Leerzeichen, um die Argumente zu trennen, für char
Escape
ty @ifree, ich habe testcomplete bekommen, um so zu laufen! Ich habe einfache Anführungszeichen um die Liste der durch Leerzeichen getrennten Argumente verwendet. [1]; aber jetzt echo $ exitcode = false, was war nicht mein Rückkehrcode aus dem Prozess? [1] $ exitCode = [Diagnostics.Process] :: Start ("c: \ Programme (x86) \ SmartBear \ TestComplete 10 \ Bin \ TestComplete.exe", '"c: \ Users \ ME \ Documents \ TestComplete 10 Projekte \ hig4TestProject1 \ hig4TestProject1.pjs "/ run / project: myProj / test:" KeywordTests | zTdd1_Good "/ exit ') .WaitForExit (60)
AnneTheAgile
3

Das Einschließen der Option -NoNewWindowgibt mir einen Fehler:Start-Process : This command cannot be executed due to the error: Access is denied.

Ich konnte es nur zum Laufen bringen, indem ich anrief:

Start-Process <path to exe> -Wait
twasbrillig
quelle
0

Wenn Sie weiter gehen, können Sie sogar spontan analysieren

z.B

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

Es gibt immer cmd. Es kann weniger ärgerlich sein, wenn Sie Probleme haben, Argumente für den Startprozess zu zitieren:

cmd /c start /wait notepad
js2010
quelle