Using Windows PowerShell Jobs

Summary: Microsoft Scripting Guy, Ed Wilson, shows an excerpt from his new step-by-step book about using Windows PowerShell jobs.

Microsoft Scripting Guy, Ed Wilson, is here. Today I want to share a portion of my new Microsoft Press Windows PowerShell 3.0 Step by Step book. This book is available now for preorder.

Using Windows PowerShell Jobs

You can begin a new Windows PowerShell job by using the Start-Job cmdlet. The command to run as a job is placed in a script block, and the jobs are sequentially named Job1, Job2 … This is shown here.

PS C:> Start-Job -ScriptBlock {get-process}
Id Name PSJobTypeName State HasMoreData Location
— —- ————- —– ———– ——–
10 Job10 BackgroundJob Running True localhost
PS C:>

The jobs receive job IDs that are also sequentially numbered. The first job created in a Windows PowerShell console is always job ID 1. You can use either the job ID or the job name to obtain information about the job. This is seen here.

PS C:> Get-Job -Name job10

Id Name PSJobTypeName State HasMoreData Location

— —- ————- —– ———– ——–

10 Job10 BackgroundJob Completed True localhost

PS C:> Get-Job -Id 10

Id Name PSJobTypeName State HasMoreData Location

— —- ————- —– ———– ——–

10 Job10 BackgroundJob Completed True localhost

PS C:>

Once you see that the job has completed, you can receive the job. The Receive-Job cmdlet returns the same information that returns if a job is not used. The Job1 output seen here (truncated to save space).

PS C:> Receive-Job -Name job10
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
62 9 1672 6032 80 0.00 1408 apdproxy
132 9 2316 5632 62 1364 atieclxx
122 7 1716 4232 32 948 atiesrxx
114 9 14664 15372 48 1492 audiodg
556 62 53928 5368 616 3.17 3408 CCC
58 8 2960 7068 70 0.19 928 conhost
32 5 1468 3468 52 0.00 5068 conhost
784 14 3284 5092 56 416 csrss
529 27 2928 17260 145 496 csrss
182 13 8184 11152 96 0.50 2956 DCPSysMgr
135 11 2880 7552 56 2056 DCPSysMgrSvc
… (truncated output)

Once a job has been received, that is itthe data is gone, unless you save it to a variable. The following code illustrates this concept.

PS C:> Receive-Job -Name job10
PS C:>

What can be confusing about this is that the job still exists, and the Get-Job cmdlet continues to retrieve information about the job. This is seen here.

PS C:> Get-Job -Id 10
Id Name PSJobTypeName State HasMoreData Location
— —- ————- —– ———– ——–
10 Job10 BackgroundJob Completed False localhost

As a best practice, use the Remove-Job cmdlet to delete remnants of completed jobs when you are finished using the job object. This will avoid confusion regarding active jobs, completed jobs, and jobs waiting to be processed. After a job has been removed, the Get-Job cmdlet returns an error if you attempt to retrieve information about the jobbecause it no longer exists. This is illustrated here.

PS C:> Remove-Job -Name job10
PS C:> Get-Job -Id 10
Get-Job : The command cannot find a job with the job ID 10. Verify the value of the
Id parameter and then try the command again.
At line:1 char:1
+ Get-Job -Id 10
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (10:Int32) [Get-Job], PSArgumentExcep
+ FullyQualifiedErrorId : JobWithSpecifiedSessionNotFound,Microsoft.PowerShell.

When working with the job cmdlets, I like to give the jobs their own name. A job that returns process objects via the Get-Process cmdlet might be called getProc. A contextual naming scheme works better than trying to keep track of names such as Job1 or Job2. Do not worry about making your job names too long, because you can use wildcard characters to simplify the typing requirement. When you receive the job, make sure you store the returned objects in a variable. This is shown here.

PS C:> Start-Job -Name getProc -ScriptBlock {get-process}
Id Name PSJobTypeName State HasMoreData Location
— —- ————- —– ———– ——–
12 getProc BackgroundJob Running True localhost
PS C:> Get-Job -Name get*
Id Name PSJobTypeName State HasMoreData Location
— —- ————- —– ———– ——–
12 getProc BackgroundJob Completed True localhost
PS C:> $procObj = Receive-Job -Name get*
PS C:>

Once you have the returned object in a variable, you can use the object with other Windows PowerShell cmdlets. One thing to keep in mind is that the object is deserialized. This is seen here where I use gm as an alias for the Get-Member cmdlet.

PS C:> $procObj | gm
TypeName: Deserialized.System.Diagnostics.Process

This means that not all the normal members from the System.Diagnostics.Process .NET Framework object are available. The normal methods are shown here (gps is an alias for the Get-Process cmdlet, gm an alias for Get-Member, and –m is enough of the –membertype parameter to distinguish it on the Windows PowerShell console line).

PS C:> gps | gm -m method
TypeName: System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
BeginErrorReadLine Method System.Void BeginErrorReadLine()
BeginOutputReadLine Method System.Void BeginOutputReadLine()
CancelErrorRead Method System.Void CancelErrorRead()
CancelOutputRead Method System.Void CancelOutputRead()
Close Method System.Void Close()
CloseMainWindow Method bool CloseMainWindow()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Dispose Method System.Void Dispose()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Kill Method System.Void Kill()
Refresh Method System.Void Refresh()
Start Method bool Start()
ToString Method string ToString()
WaitForExit Method bool WaitForExit(int milliseconds), System.Void WaitForExit()
WaitForInputIdle Method bool WaitForInputIdle(int milliseconds), bool WaitForInputIdle()

Methods from the deserialized object are shown here where I use the same command I used previously.

PS C:> $procObj | gm -m method
TypeName: Deserialized.System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
ToString Method string ToString(), string ToString(string format, System.IFormatProvider formatProvider)
PS C:>

Join me tomorrow for a guest post by Microsoft MVP and Honorary Scripting Guy Don Jones on new year’s resolutions.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Comments (2)

  1. Peter Parker says:

    This is so much fun, I am very excited to be a part of all the tech available. Thank you from a devoted friend 🙂

  2. John Wheatley says:

    It may be obvious to some, but a rather important limitation, worth mentioning I would have thought, is that the scope and lifetime of this mechanic is that of the current PowerShell session. Still a useful mechanic, just not quite as much as it could
    have been.