Running Background Processes in Powershell
By Andrew Kulpa on April 25, 2020
Suppose we’ve been tasked to run a long-running process on a client machine. We have many other tasks we need to complete in their environment, but we know that this one will take what seems like forever. Although we can open another PowerShell console session, we miss out on any variables in the current session.
As you might have guessed by the title, the solution to this problem is to use PowerShell background jobs! In PowerShell, a background job is a command that runs in the background. A good way to see all the cmdlets that can be used for jobs is to run a Get-Command
for the Noun
Job
like so:
PS C:\Users\andrew.kulpa> Get-Command -Noun Job
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Debug-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Receive-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Remove-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Resume-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Start-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Stop-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Suspend-Job 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Wait-Job 3.0.0.0 Microsoft.PowerShell.Core
In this article, the cmdlets we will go over are Start-Job
, Get-Job
, and Receive-Job
.
Starting Background Jobs
To start a background job, we use the Start-Job
PowerShell cmdlet. A background job executes a given command outside the current session. The following is a fairly simple example where we accumulate a count of all file types recursively in the current directory:
# You can also provide input using the $input variable and an -Input argument!
# This could then be used as the first argument instead of ".\".
PS C:\Users\andrew.kulpa\Documents> Start-Job -ScriptBlock {
>> Get-Childitem ".\" -Recurse |
>> where { -not $_.PSIsContainer } |
>> group Extension -NoElement |
>> sort count -Desc
>> }
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
7 Job7 BackgroundJob Running True localhost ...
As we saw, the output of the Start-Job
cmdlet is an object that describes the background job. Most of these object parameters are quite useful. One thing we can note is that we should be able to set the Name
of a job by invoking the cmdlet with a value passed to -Name
.
Checking and Receiving Jobs
In the last section we learned how to start a background job. Obviously with the job is running the background the job status could change at any time. We also want to get the output of the process! Let’s do this by starting another job to try out passing a value to -Name
and then explore multiple ways of using the Get-Job
command.
First, let’s run the same command with the value fileCounts
passed into -Name
:
PS C:\Users\andrew.kulpa\Documents> Start-Job -Name fileCounts -ScriptBlock {
>> Get-Childitem ".\" -Recurse |
>> where { -not $_.PSIsContainer } |
>> group Extension -NoElement |
>> sort count -Desc
>> }
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
9 fileCounts BackgroundJob Running True localhost ...
That wasn’t much different, but we can clearly see that the value in Name
is set to fileCounts
! With that set, if we were to return to this machine we know what the background job was doing. Now lets try out the Get-Job
cmdlet:
PS C:\Users\andrew.kulpa\Documents> Get-Job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
7 Job7 BackgroundJob Completed False localhost ...
9 fileCounts BackgroundJob Completed True localhost ...
It returns pretty much the same output as before, but it includes Job7
and has a State
of Completed
. If we wanted to filter the results of Get-Job
we can also filter by setting a value for either -Id
or -Name
:
PS C:\Users\andrew.kulpa\Documents> Get-Job -Id 9
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
9 fileCounts BackgroundJob Completed True localhost ...
PS C:\Users\andrew.kulpa\Documents> Get-Job -Name fileCounts
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
9 fileCounts BackgroundJob Completed True localhost ...
That’s great that we can now run a background job and check the status, but its likely we also want the output of the command that was ran. To do this, we’ll use Receive-Job
which has the same filters we used earlier. Let’s run the cmdlet for the first job we ran:
PS C:\Users\andrew.kulpa\Documents> Receive-Job -Id 7
Count Name
----- ----
1 .txt
1 .rtf
Awesome! Is that it? Maybe. Would running it again give us the same output? Lets try:
PS C:\Users\andrew.kulpa\Documents> Receive-Job -Id 7
PS C:\Users\andrew.kulpa\Documents>
That’s a big no. To keep the output of the job we need to add the -Keep
parameter. Let’s try that again with the fileCounts
background job:
PS C:\Users\andrew.kulpa\Documents> Receive-Job -Name fileCounts -Keep
Count Name
----- ----
1 .txt
1 .rtf
PS C:\Users\andrew.kulpa\Documents> Receive-Job -Name fileCounts -Keep
Count Name
----- ----
1 .txt
1 .rtf
Well there we go! We have successfully starting a background job, checked the status of the job, and received the output of the job. There are many cmdlets we have not gone over yet, so if you’re interested in learning more check out the cmdlets we saw earlier and explore the about_jobs documentation!
Further Reading
Documentation: