289

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"
0

11 Answers 11

478

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
Sign up to request clarification or add additional context in comments.

4 Comments

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 "<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.
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 requests
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.
67

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.

1 Comment

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.
15

Just use "Wait-process" :

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

job is done

3 Comments

It doesn't work with PowerShell 7.1.4 (on Windows 10).
you need -PassThru, otherwise it won't wait
Aside from missing -PassThru, this will launch the programs in parallel rather than sequentially; the latter is what the question requests.
10

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

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

2 Comments

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.
Even -passthru doesn't capture the standardoutput or standarderror, but at least it has the exitcode.
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>)

4 Comments

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.
6

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

Comments

5

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://stackoverflow.com/a/7272390/254276

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

Comments

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

1 Comment

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

Comments

1

There's always cmd.

cmd /c start /wait notepad

Or

notepad | out-host
powershell notepad | write-output  # pipe to anything
powershell notepad > $null

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 un_a.exe does this in foldingathome, notepad++, psychopy, ciscoamp, firefox, scratch), it still waits.

start-process -wait powershell notepad

This waits in windows 10 but not windows 11:

cmd /c notepad

Comments

0

Note that you're describing unconditional sequencing of commands, whereas the && operator is conditional in that it only executes the RHS command if the LHS one indicated success.

  • For unconditional sequencing (the equivalent of cmd.exe's & operator), use ;, PowerShell's statement separator (<cmd1> and <cmd2> are placeholders for arbitrary commands):

    # First run <cmd1> to completion, then <cmd2>
    <cmd1>; <cmd2>
    
    • Note:

      • If the commands are external executables, the assumptions is that they're console applications, which PowerShell executes synchronously, resulting in the desired sequencing (first run <cmd1> to completion, then <cmd2>).

      • If they're GUI applications, which execute asynchronously by default, you can use the approaches shown in Keith's answer, <cmd1> | Out-Null, or Start-Process -Wait <cmd1>; the latter, which requires specifying pass-through arguments separately, has the advantage that it also works for GUI applications that use a short-lived startup process that defers to a child process; however, even that may not be sufficient for modern UWP applications; see this answer for more information.

  • For conditional sequencing, whether with && logic or its negation, ||:

    • Only PowerShell (Core) v7 supports && and ||, the pipeline-chaining operators:

      # Run <cmd1> to completion and only if it *succeeded* run <cmd2>.
      <cmd1> && <cmd2> 
      
      # Run <cmd1> to completion and only if it *failed* run <cmd2>.
      <cmd1> || <cmd2> 
      
      • Notable pitfalls are:

        • These operators do not work with PowerShell-native Test- commands (because they report a Boolean value as output rather than as their execution success status).
        • Language statements such as exit and throw on the RHS must be wrapped in $(...).
    • In Windows PowerShell (the legacy, ships-with-Windows edition whose latest and final version is 5.1), you must emulate these operators:

      # Emulate `&&`:
      # Run <cmd1> to completion and only if it *succeeded* run <cmd2>.
      <cmd1>; if ($?) { <cmd1> } 
      
      # Emulate `||`:
      # Run <cmd1> to completion and only if it *failed* run <cmd2>.
      <cmd1>; if (-not $?) { <cmd2> } 
      
      • Notable pitfall:

        • With external programs, if a 2> redirection is involved, $? can yield false negatives;[1] therefore, it is more robust to use the following:

           # With EXTERNAL PROGRAMS only:
          
           # Emulate `&&`:
           # Run <cmd1> to completion and only if it *succeeded* run <cmd2>.
           <cmd1>; if ($LASTEXITCODE -eq 0) { <cmd1> } 
          
           # Emulate `||`:
           # Run <cmd1> to completion and only if it *failed* run <cmd2>.
           <cmd1>; if ($LASTEXITCODE -ne 0) { <cmd2> } 
          
    • See this answer for more information.


[1] This problem has been fixed in PowerShell (Core) 7; however, there it is simpler to use && and || directly.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.