I am having trouble getting the PID from a Start-Process within a Start-Job in Powershell (latest version). The Start-Process cmdlet runs and returns the PID as expected when run outside of the Start-Job Scriptblock. When the statement is added to the Start-Job Scriptblock, no PID is returned. Can someone point this newbie in the right direction?
$myJob = Start-Job -Name newJob -ScriptBlock {
$procID = (Start-Process myprogram.exe -PassThru -ArgumentList "myArgs" -WorkingDirectory "myWorkingDir").Id
}
Perhaps you think that assigning to a variable in a script block being executed in a background job makes that variable visible to the caller, which is not the case.
Background jobs run in an entirely separate session, and, in order to communicate data to the caller, the success output stream must be used, whose content the caller must collect via the Receive-Job cmdlet:
$myJob = Start-Job -Name newJob -ScriptBlock {
# Implicitly output the PID of the asynchronously launched process,
# by using -PassThru and returning the output object's .Id property value.
# Note: By default, Start-Process runs the process
# *asynchronously*, in a *new window*.
(
Start-Process myprogram.exe -PassThru -ArgumentList "myArgs" -WorkingDirectory "myWorkingDir"
).Id
}
# Use Receive-Job to collect the background job's output, once available,
# then remove the job automatically.
# Note: The process launched *asynchronously* from the background job
# with Start-Process lives on.
$procId = Receive-Job -Wait -AutoRemoveJob
Taking a step back: Start-Process is itself asynchronous, so there's no need to use a background job: just start myprogram.exe directly in the caller's context:
# Launch myprogram.exe asynchronously, in a new window.
# $proc receives a value once the program has *launched* and
# your script continues right after.
$proc = Start-Process myprogram.exe -PassThru -ArgumentList "myArgs" -WorkingDirectory "myWorkingDir"
# $proc.Id retrieves the PID (process ID)
# $proc.HasExited tells you whether the process has exited.
# $proc.WaitForExit() allows you to wait synchronously for the process to exit.
# * If you intend to wait *right away*, i.e run in a new window, but
# in a blocking fashion, you can omit -PassThru and pass -Wait instead.
However, if myprogram.exe is a console (terminal) application that you want to run asynchronously and whose output you want to capture, do use Start-Job, but don't use Start-Process to launch myprogram.exe: invoke it directly from the background job:
$myJob = Start-Job -Name newJob -ScriptBlock {
Set-Location "myWorkingDir"
# Launch console app myprogram.exe *synchronously*, *invisibly*,
# and relay its (stdout) output as the job's output.
myprogram.exe
}
# ... do other things
# Wait for myprogram.exe to finish, at which point the
# job exits, relaying its (stdout) output.
$myJob | Receive-Job -Wait -AutoRemoveJob
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With