Handle Long PowerShell Scripts With Background Jobs

PowerShell expert Brien Posey demonstrates a technique for managing long-running scripts.

Brien Posey

August 12, 2024

9 Min View
ITPro Today

In this tutorial, Brien Posey explains how to handle long-running PowerShell scripts using background jobs. He demonstrates how to create, monitor, and retrieve results from background jobs using the Start-Job, Receive-Job, and Stop-Job cmdlets, ensuring scripts run efficiently without blocking the PowerShell session. He also shows how to integrate jobs into a script.

The following transcript has been lightly edited for clarity and length.

Transcript:

Brien Posey: Hello, greetings and welcome. I'm Brien Posey. Today, I want to show you a technique for dealing with long-running PowerShell scripts.

Now, if you have a PowerShell script that takes a long time to run, there are a couple of different ways that you can deal with it.

  1. Progress Bar: You can create a progress bar that slowly inches its way across the screen so that you can keep track of how long the script is taking to run. That's certainly a viable option. There are plenty of scripts that do that.

  2. Background Jobs: Another option is to create a job. Now, what a job does is it allows the long-running scripts to run in the background. The advantage of this is that it doesn't tie up your PowerShell session. You can work on other things while your script progresses in the background.

Creating a Job

Related:Getting Started Using GitHub Copilot for PowerShell Scripting

Let's look at how this works.

Suppose we want to create a script that counts from one up to 10 million. As it counts each number, it adds it to a sum total so that you're eventually left with the sum of every number between one and 10 million. Well, as you can imagine, depending on the speed of your machine, a script like that could take some time to complete.

What I'm going to do is create a variable that I'll call $Job. I'm going to set that equal to Start-Job.

$Job = Start-Job

Start-Job is the native PowerShell cmdlet that defines a job.

Now we need to tell PowerShell what will happen as a part of that job. So, I'm going to create a -ScriptBlock. Then, within that -ScriptBlock, I’ll set up a counter that counts from one to 10 million. I think I'll make it 100 million for this particular example. I'm going to add a pipe symbol. I'll type Measure-Object, -Sum, and a closing bracket.

$Job = Start-Job -ScriptBlock {1..100000000 | Measure-Object -Sum }

When I press Enter, you can see that I'm returned to the PowerShell prompt almost immediately. What's happening is that I have created a script. That script is running, but it's running in the background, so I still have full control over the PowerShell environment.

Monitoring Job Status

So, how do we check our script to see if it's still running? Well, let's look at the $Job variable. Even though we created a job, remember we mapped it to the variable $Job. That variable works just like any other variable.

Related:How To Add Exit Mechanisms in PowerShell Scripts

I'll type $Job. We can see the job ID, the job name, the PowerShell job type name, and the state. The state indicates that this job is still running.

Anytime we want to check the state of this job, we can type $Job.State and press Enter. And we can see that the job is indeed still running.

Incidentally, PowerShell also shows us the location where the job is running. In this case, we're running on a local host. Additionally, we can see the command that is associated with the job. In this case, we count from one to 100 million and then add all those numbers together.

Stopping a Job

What if a job takes too long and we want to cancel it? We would type Stop-Job and specify the variable associated with that job – $Job.

Stop-Job $Job

Presumably, the job has been terminated at this point. If we want to check on it, we can type $Job.State. And now we can see that the job has stopped.

So, that's how you can interrupt a job that's taking too long to run.

Handling Job Output

Now, what about the job output? How would you handle that? Well, you generally handle output by displaying the contents of a variable.

Let me show you how this works. What I'm going to do is start a new job. In the interest of time, I’ll make this a little bit smaller in number. We'll get rid of a couple of zeros.

Related:Building Graphical PowerShell Tools: A Three-Part Guide

$Job = Start-Job -ScriptBlock {1..1000000 | Measure-Object -Sum }

And I'll go ahead and press Enter. Let's check how long the job is going to take to run. So, I'm going to type $Job.State. And the job has already been completed.

So, how do we get the results? Remember, we're taking all those numbers that we counted and adding them together to get a sum. So, that sum is what we really want out of this block of code. Well, the way that we do that is to set up another variable. I'm going to type $Result. $Result is the name of a variable I'm creating to store the results of my Measure-Object command. And I'll set that equal to Receive-Job. Then we have to specify the job name: $Job.

$Result = Receive-Job $Job

I'll press Enter. Then, if I type $Result, I can see the sum right there.

And if I wanted to get just the specific number I'm after, I could type $Result.sum. Then I would get the result that I'm interested in.

Integrating Jobs Into a Script

So, that's just kind of how a job works, but I want to show you how all this is integrated into a script.

What I've done is I've created a script called Jobs.ps1.

$job =  Start-Job -ScriptBlock {1..10000000 | Measure-Object -Sum }
# Wait for the job to complete
while ($job.state -eq ‘Running’) {
                  Write-Output “Job is still running…”
                  Start-Sleep -Seconds 5
                  $job = Get-Job -Id $job.Id
}
# Get the results
$result = Receive-Job -Job $job
$ Display the result
Write-Output “The sum is: $($result.Sum)
$ Remove the job
Remove-Job -Job $job

I start out by creating a variable called $job and set it equal to Start-Job. Then the script block is exactly what you saw a moment ago. We're counting from one to a large number. Then I'm using Measure-Object to create a sum of all the numbers I've added together.

The next thing I'm doing is waiting for the job to be completed. The nice thing about jobs is that they run in the background. But suppose you didn't want to monitor a running job. Well, what you see is one way of doing that. I've set up a While loop, and I'm checking the $job.State to see if it is equal to Running. If the job is still running, I’ll run the block of code that displays a line of text saying, “Job is still running….” Then we’ll start a sleep timer that will count for five seconds. Once five seconds is up, we’ll set up a variable $job set to equal to Get-Job -Id $job.Id. And then the loop starts all over again.

Eventually, this job will finish running, and at that point, we want to see the results. We're doing exactly what I did a moment ago. I'm setting up a variable called $result equal to Receive-Job. That's the command line that takes the output from a job. Then we have to specify the -Job. The job that we're looking at is $job.

To display the results, all I'm doing is using the Write-Output cmdlet. I'm typing, “The sum is: $($result.Sum)”.

At the end of the script, I've got one last cmdlet I haven't shown you yet: Remove-Job. I specify the job that I want to remove.

So, let's go ahead and run our script. I'm going to switch over to PowerShell and type:

./jobs.ps1

I'll press Enter. I'll go ahead and run the script. You can see text saying, “Job is still running….” It's going to wait five seconds and check again. Eventually, the job is completed and we can see the sum total.

So, that's how you can run a long-running process inside a job.

I'm Brien Posey. Thanks for watching.

About the Author

Brien Posey

Brien Posey is a bestselling technology author, a speaker, and a 20X Microsoft MVP. In addition to his ongoing work in IT, Posey has spent the last several years training as a commercial astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space.

https://brienposey.com/

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like