224

In bash the ampersand (&) can be used to run a command in the background and return interactive control to the user before the command has finished running. Is there an equivalent method of doing this in Powershell?

Example of usage in bash:

 sleep 30 &
Eddie Groves
  • 33,851
  • 14
  • 47
  • 48

8 Answers8

169

As long as the command is an executable or a file that has an associated executable, use Start-Process (available from v2):

Start-Process -NoNewWindow ping google.com

You can also add this as a function in your profile:

function bg() {Start-Process -NoNewWindow @args}

and then the invocation becomes:

bg ping google.com

In my opinion, Start-Job is an overkill for the simple use case of running a process in the background:

  1. Start-Job does not have access to your existing scope (because it runs in a separate session). You cannot do "Start-Job {notepad $myfile}"
  2. Start-Job does not preserve the current directory (because it runs in a separate session). You cannot do "Start-Job {notepad myfile.txt}" where myfile.txt is in the current directory.
  3. The output is not displayed automatically. You need to run Receive-Job with the ID of the job as parameter.

NOTE: Regarding your initial example, "bg sleep 30" would not work because sleep is a Powershell commandlet. Start-Process only works when you actually fork a process.

Bogdan Calmac
  • 7,993
  • 6
  • 51
  • 64
  • 13
    Glad I found this answer. I was looking for the equivalent of a Unix fork-twice mechanism. To me it seems that something started with `Start-Job` will be killed when the PS shell exits. In contrast it seems that something started with `Start-Process` will continue to run after the PS shell exits. This is a **major** difference. – peterh Nov 08 '13 at 09:32
  • Yes - if you start a script using `Start-Process`, it will survive the shell termination, but if you started it from a console window then it stays bound to that window and closing the window will terminate the process. – Guss Aug 10 '15 at 13:31
  • 2
    Note however that you cannot do output redirection with `Start-Process` and so this will NOT work: `Start-Process {ping -n 1000 example.com > ping__example.com.txt }`. Same thing with `Start-Job` works fine (though you have to use full path to the output file). – Nux Mar 03 '16 at 15:07
  • 1
    Attempted to do this to run a node web server, and the process terminates when I exit powershell. Anyone know why? – Jel Oct 20 '16 at 19:12
  • @Jel No, but Guss's comment talks about that. – jpaugh Feb 07 '17 at 18:13
  • I wish that worked with scripts. Wait I got something. – js2010 Jan 02 '19 at 18:01
  • start-process powershell -NoNewWindow -ArgumentList '-file .\loop.ps1' – js2010 Jan 02 '19 at 18:09
  • 1
    I'm assuming the first line means I can't use it for cmdlets? – Gert van den Berg Apr 26 '19 at 06:35
  • In case you also need the PID -- e.g. a poor man's linux _timeout_ `$x = Start-Process -Filepath "ping" -ArgumentList 'google.com -n 100' -NoNewWindow -PassThru; Start-Sleep -Seconds 5; try { Stop-Process -Id $x.Id -ErrorAction stop } catch {};` This will ping for 5 seconds then kill the process if it is still running. If you need an early termination you can change Start-Sleep to a loop that continues or breaks instead. – Brian H. Dec 18 '19 at 18:32
  • Running without `-NoNewWindow` you can see the startpath of the programme.Use `@args` instead of `$args` as Bogdan wrote in his answer because you want a string argument, not an object! – Timo Oct 28 '20 at 13:47
59

From PowerShell Core 6.0 you are able to write & at end of command and it will be equivalent to running you pipeline in background in current working directory.

It's not equivalent to & in bash, it's just a nicer syntax for current PowerShell jobs feature. It returns a job object so you can use all other command that you would use for jobs. For example Receive-Job:

C:\utils> ping google.com &

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
35     Job35           BackgroundJob   Running       True            localhost            Microsoft.PowerShell.M...


C:\utils> Receive-Job 35

Pinging google.com [172.217.16.14] with 32 bytes of data:
Reply from 172.217.16.14: bytes=32 time=11ms TTL=55
Reply from 172.217.16.14: bytes=32 time=11ms TTL=55
Reply from 172.217.16.14: bytes=32 time=10ms TTL=55
Reply from 172.217.16.14: bytes=32 time=10ms TTL=55

Ping statistics for 172.217.16.14:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 10ms, Maximum = 11ms, Average = 10ms
C:\utils>

If you want to execute couple of statements in background you can combine & call operator, { } script block and this new & background operator like here:

& { cd .\SomeDir\; .\SomeLongRunningOperation.bat; cd ..; } &

Here's some more info from documentation pages:

from What's New in PowerShell Core 6.0:

Support backgrounding of pipelines with ampersand (&) (#3360)

Putting & at the end of a pipeline causes the pipeline to be run as a PowerShell job. When a pipeline is backgrounded, a job object is returned. Once the pipeline is running as a job, all of the standard *-Job cmdlets can be used to manage the job. Variables (ignoring process-specific variables) used in the pipeline are automatically copied to the job so Copy-Item $foo $bar & just works. The job is also run in the current directory instead of the user's home directory. For more information about PowerShell jobs, see about_Jobs.

from about_operators / Ampersand background operator &:

Ampersand background operator &

Runs the pipeline before it in a PowerShell job. The ampersand background operator acts similarly to the UNIX "ampersand operator" which famously runs the command before it as a background process. The ampersand background operator is built on top of PowerShell jobs so it shares a lot of functionality with Start-Job. The following command contains basic usage of the ampersand background operator.

Get-Process -Name pwsh &

This is functionally equivalent to the following usage of Start-Job.

Start-Job -ScriptBlock {Get-Process -Name pwsh}

Since it's functionally equivalent to using Start-Job, the ampersand background operator returns a Job object just like Start-Job does. This means that you are able to use Receive-Job and Remove-Job just as you would if you had used Start-Job to start the job.

$job = Get-Process -Name pwsh &
Receive-Job $job

Output

NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
------    -----      -----     ------      --  -- -----------
    0     0.00     221.16      25.90    6988 988 pwsh
    0     0.00     140.12      29.87   14845 845 pwsh
    0     0.00      85.51       0.91   19639 988 pwsh


$job = Get-Process -Name pwsh &
Remove-Job $job

For more information on PowerShell jobs, see about_Jobs.

Mariusz Pawelski
  • 25,983
  • 11
  • 67
  • 80
  • Any idea how to handle *console* jobs? I tried the above with `Receive-Job 3`, but nothing happens. – not2qubit Feb 02 '19 at 14:18
  • @not2qubit I'm not sure what you mean by "_console_ jobs". What command do you run that you want to run in background? – Mariusz Pawelski Feb 05 '19 at 21:42
  • I was using an app that launches a windows console, but I can not get any output and was hoping to get it similarly to the *nix world `fg` for bringing job to the *foreground*. – not2qubit Feb 05 '19 at 23:04
  • 1
    @not2qubit Unfortunately it seems that there is no such thing similar to unix `fg` in powershell :( I remember looking for it, but couldn't find anything – Mariusz Pawelski Feb 05 '19 at 23:32
35

Seems that the script block passed to Start-Job is not executed with the same current directory as the Start-Job command, so make sure to specify fully qualified path if needed.

For example:

Start-Job { C:\absolute\path\to\command.exe --afileparameter C:\absolute\path\to\file.txt }
Gian Marco
  • 22,140
  • 8
  • 55
  • 44
23

You can use PowerShell job cmdlets to achieve your goals.

There are 6 job related cmdlets available in PowerShell.

  • Get-Job
    • Gets Windows PowerShell background jobs that are running in the current session
  • Receive-Job
    • Gets the results of the Windows PowerShell background jobs in the current session
  • Remove-Job
    • Deletes a Windows PowerShell background job
  • Start-Job
    • Starts a Windows PowerShell background job
  • Stop-Job
    • Stops a Windows PowerShell background job
  • Wait-Job
    • Suppresses the command prompt until one or all of the Windows PowerShell background jobs running in the session are complete

If interesting about it, you can download the sample How to create background job in PowerShell

Eric
  • 949
  • 8
  • 7
23
ps2> start-job {start-sleep 20}

i have not yet figured out how to get stdout in realtime, start-job requires you to poll stdout with get-job

update: i couldn't start-job to easily do what i want which is basically the bash & operator. here's my best hack so far

PS> notepad $profile #edit init script -- added these lines
function beep { write-host `a }
function ajp { start powershell {ant java-platform|out-null;beep} } #new window, stderr only, beep when done
function acjp { start powershell {ant clean java-platform|out-null;beep} }
PS> . $profile #re-load profile script
PS> ajp
Dustin Getz
  • 21,282
  • 15
  • 82
  • 131
  • 1
    As of PowerShell v3.0 you can get stdout in realtime like this: `Start-Job { Write-Output 'Hello world' } | Receive-Job -Wait` – JamesQMurphy Dec 07 '14 at 22:56
8

You can do something like this.

$a = start-process -NoNewWindow powershell {timeout 10; 'done'} -PassThru

And if you want to wait for it:

$a | wait-process

Bonus osx or linux version:

$a = start-process pwsh '-c',{start-sleep 5; 'done'} -PassThru 

Example pinger script I have. The args are passed as an array:

$1 = start -n powershell pinger,comp001 -pa
evandrix
  • 6,041
  • 4
  • 27
  • 38
js2010
  • 23,033
  • 6
  • 64
  • 66
5

tl;dr

Start-Process powershell { sleep 30 }
0

I've used the solution described here http://jtruher.spaces.live.com/blog/cns!7143DA6E51A2628D!130.entry successfully in PowerShell v1.0. It definitely will be easier in PowerShell v2.0.

Jeffery Hicks
  • 301
  • 1
  • 5