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 &
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:
Start-Job does not have access to your existing scope (because it runs in a separate session). You cannot do "Start-Job {notepad $myfile}" 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. 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.
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.
Receive-Job 3
, but nothing happens.
fg
for bringing job to the *foreground.
fg
in powershell :( I remember looking for it, but couldn't find anything
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 }
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
Start-Job { Write-Output 'Hello world' } | Receive-Job -Wait
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
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
Gets the results of the Windows PowerShell background jobs in the current session
Remove-Job Deletes a Windows PowerShell background job
Deletes a Windows PowerShell background job
Start-Job Starts a Windows PowerShell background job
Starts a Windows PowerShell background job
Stop-Job Stops a Windows PowerShell background 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
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
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
start
will fork the process and return control to the caller. More info here
tl;dr
Start-Process powershell { sleep 30 }
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.
Success story sharing
Start-Job
will be killed when the PS shell exits. In contrast it seems that something started withStart-Process
will continue to run after the PS shell exits. This is a major difference.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.Start-Process
and so this will NOT work:Start-Process {ping -n 1000 example.com > ping__example.com.txt }
. Same thing withStart-Job
works fine (though you have to use full path to the output file).