How to tell PowerShell to wait for each command to end before starting the next?
Asked Answered
H

10

278

I have a PowerShell 1.0 script to just open a bunch of applications. The first is a virtual machine and the others are development applications. I want the virtual machine to finish booting before the rest of the applications are opened.

In bash I could just say "cmd1 && cmd2"

This is what I've got...

C:\Applications\VirtualBox\vboxmanage startvm superdooper
    &"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
Homegrown answered 16/11, 2009 at 11:4 Comment(0)
M
4

The question was asked long ago, but since answers here are kind of references, I may mention an up to date usage. With the current implementation of PowerShell (it's 7.2 LTS as of writing) you can use && as you would do in Bash.

Conditionally execute the right-hand side pipeline based on the success of the left-hand side pipeline.

   # If Get-Process successfully finds a process called notepad,
   # Stop-Process -Name notepad is called
   Get-Process notepad && Stop-Process -Name notepad

Further info on documentation

Mcilwain answered 12/9, 2022 at 20:1 Comment(0)
S
466

Normally, for internal commands PowerShell does wait before starting the next command. One exception to this rule is external Windows subsystem based EXE. The first trick is to pipeline to Out-Null like so:

Notepad.exe | Out-Null

PowerShell will wait until the Notepad.exe process has been exited before continuing. That is nifty but kind of subtle to pick up from reading the code. You can also use Start-Process with the -Wait parameter:

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

If you are using the PowerShell Community Extensions version it is:

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

Another option in PowerShell 2.0 is to use a background job:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Stemson answered 16/11, 2009 at 15:8 Comment(4)
As a side note, if you need to pass multiple arguments with -ArgumentList, separate them with commas like -ArgumentList /D=test,/S.Treva
Thank you for the simple "<path to exe> | Out-Null" solution! The problem with "the Start-Process <path to exe> -NoNewWindow -Wait" method is that the PowerShell pauses until all child processes spawned by the parent are complete, even if the parent terminates before them. This caused our setup program issues.Emmott
This is what I use to wait for a VM to start Start-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VmName while((Get-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VmName -Status | ` select -ExpandProperty Statuses | ` ?{ $_.Code -match "PowerState" } | ` select -ExpandProperty DisplayStatus) -ne "VM running") { Start-Sleep -s 2 } Start-Sleep -s 5 ## Give the VM time to come up so it can accept remote requestsReid
Sorry for zombie comment but in case anyone is trying to duplicate this for VMware, grab PowerCLI and use Wait-Tools or a do-while with Invoke-VMScript until you get the desired response.Elianaelianora
I
65

Besides using Start-Process -Wait, piping the output of an executable will make Powershell wait. Depending on the need, I will typically pipe to Out-Null, Out-Default, Out-String or Out-String -Stream. Here is a long list of some other output options.

# 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

I do miss the CMD/Bash style operators that you referenced (&, &&, ||). It seems we have to be more verbose with Powershell.

Inalienable answered 1/9, 2011 at 15:11 Comment(1)
Note that no extra work is needed to execute console applications synchronously - as in any shell, that is the default behavior. Piping to Out-String changes the output to a single, multi-line string, whereas PowerShell by default returns an array of lines. Start-Process should be avoided for console applications (unless you truly want to run them in a new window) because you won't be able to capture or redirect their output.Redmond
A
15

Just use "Wait-process" :

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

job is done

Aircondition answered 26/6, 2014 at 20:0 Comment(2)
It doesn't work with PowerShell 7.1.4 (on Windows 10).Palmar
you need -PassThru, otherwise it won't waitFrenchy
Q
10

If you use Start-Process <path to exe> -NoNewWindow -Wait

You can also use the -PassThru option to echo output.

Quean answered 20/2, 2013 at 16:2 Comment(2)
Note that -PassThru doesn't echo output (a non-console-application by definition won't produce console output), it outputs a System.Diagnostics.Process instance that represents the newly launched process, so you can examine its properties and wait for it to exit later.Redmond
Even -passthru doesn't capture the standardoutput or standarderror, but at least it has the exitcode.Antepast
R
8

Some programs can't process output stream very well, using pipe to Out-Null may not block it.
And Start-Process needs the -ArgumentList switch to pass arguments, not so convenient.
There is also another approach.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
Ribose answered 26/3, 2014 at 6:53 Comment(4)
how do multiple arguments work in that call? how are they delimited? how does one handle various nested string escaping? ty!Carrnan
@Carrnan doc use space to separate the arguments, for char escaping use backslashRibose
ty @ifree, I did get testcomplete to run that way! I used single quotes around the list of space delimited arguments.[1]; but now echo $exitcode=false, which wasn't my returncode from the process? [1] $exitCode = [Diagnostics.Process]::Start( "c:\Program Files (x86)\SmartBear\TestComplete 10\Bin\TestComplete.exe" ,'"c:\Users\ME\Documents\TestComplete 10 Projects\hig4TestProject1\hig4TestProject1.pjs" /run /project:myProj/test:"KeywordTests|zTdd1_Good" /exit' ).WaitForExit(60)Carrnan
Start-Process -Wait took a long time (like a minute) to return to script execution after closing the application. This took a couple of seconds.Oldham
M
4

Including the option -NoNewWindow gives me an error: Start-Process : This command cannot be executed due to the error: Access is denied.

The only way I could get it to work was to call:

Start-Process <path to exe> -Wait
Maleficence answered 31/12, 2014 at 0:54 Comment(1)
Usually means that it needs to run as admin. Admin privilege escalation needs to open a new window. It's not possible to connect an Admin command to a non admin console.Honorary
M
4

The question was asked long ago, but since answers here are kind of references, I may mention an up to date usage. With the current implementation of PowerShell (it's 7.2 LTS as of writing) you can use && as you would do in Bash.

Conditionally execute the right-hand side pipeline based on the success of the left-hand side pipeline.

   # If Get-Process successfully finds a process called notepad,
   # Stop-Process -Name notepad is called
   Get-Process notepad && Stop-Process -Name notepad

Further info on documentation

Mcilwain answered 12/9, 2022 at 20:1 Comment(0)
A
4

Building upon @Justin & @Nathan Hartley 's answers:

& "my.exe" | Out-Null    #go nowhere    
& "my.exe" | Out-Default # go to default destination  (e.g. console)
& "my.exe" | Out-String  # return a string

the piping will return it in real-time

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

Note: If the executed program returns anything other than a 0 exitcode, the piping will not work. You can force it to pipe with redirection operators such as 2>&1

& "my.exe" 2>&1 | Out-String

sources:

https://mcmap.net/q/108076/-how-to-tell-powershell-to-wait-for-each-command-to-end-before-starting-the-next

https://social.technet.microsoft.com/forums/windowsserver/en-US/b6691fba-0e92-4e9d-aec2-47f3d5a17419/start-process-and-redirect-output-to-powershell-window

Afterdamp answered 5/10, 2022 at 13:44 Comment(0)
M
2

Taking it further you could even parse on the fly

e.g.

& "my.exe" | %{
    if ($_ -match 'OK')
    { Write-Host $_ -f Green }
    else if ($_ -match 'FAIL|ERROR')
    { Write-Host $_ -f Red }
    else 
    { Write-Host $_ }
}
Maurya answered 11/4, 2018 at 14:9 Comment(0)
A
0

There's always cmd.

cmd /c start /wait notepad

Or

notepad | out-host

Here's another odd one. Start-process keeps waiting for notepad, even though the 2nd powershell exits. ("cmd /c start /wait powershell notepad" doesn't do this.) So even if anything in the process tree runs in the background (the NSIS uninstaller does this), it still waits.

start-process -wait powershell notepad
Antepast answered 10/12, 2019 at 15:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.