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"
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
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.
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.
Just use "Wait-process" :
"notepad","calc","wmplayer" | ForEach-Object {Start-Process $_} | Wait-Process ;dir
job is done
If you use Start-Process <path to exe> -NoNewWindow -Wait
You can also use the -PassThru
option to echo output.
-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.
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>)
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
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 $_ }
}
There's always cmd.
cmd /c start /wait notepad
Or
notepad | out-host
Success story sharing
| out-null
did just what I needed. Tried usingStart-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...-ArgumentList
, separate them with commas like-ArgumentList /D=test,/S
.