ChatGPT解决这个技术问题 Extra ChatGPT

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

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"

C
Cristian Ciupitu

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

My bad. Start-Process -Wait works great, but now I see it this is not what I was looking for... I'm actually seeking to wait until the vm to finishes booting. I imagine that's going to be tough. I suppose I'll have to find a new thread for that. Thanks but.
Brilliant, | out-null did just what I needed. Tried using Start-Job but because I'm passing the results of functions as parameters, it got a little skitzoid on me, so I couldn't use the last suggestion...
As a side note, if you need to pass multiple arguments with -ArgumentList, separate them with commas like -ArgumentList /D=test,/S.
Thank you for the simple " | Out-Null" solution! The problem with "the Start-Process -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.
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.
C
Cristian Ciupitu

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.


This is an incredibly good solution if you need to parse the output!
Pointing out Out-xxxx redirectors list quickly allowed me understand why I should use Out-Default instead of Out-Null, since I needed to keep console output.
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.
Actually, whether a native command runs synchronously (with output) or asynchronously depends on the executable. As an example, without capturing the output, the following only outputs "bingo". & 'C:\Program Files\Mozilla Firefox\firefox.exe' -? ; 'bingo'
F
Flaviofire

Just use "Wait-process" :

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

job is done


It doesn't work with PowerShell 7.1.4 (on Windows 10).
S
Sandy Chapman

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

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


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.
I
Igor Popov

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>)

how do multiple arguments work in that call? how are they delimited? how does one handle various nested string escaping? ty!
@AnneTheAgile doc use space to separate the arguments, for char escaping use backslash
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)
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.
t
twasbrillig

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

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.
J
Justin

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 $_ }
}

j
js2010

There's always cmd.

cmd /c start /wait notepad

Or

notepad | out-host