'How to modify the output of PowerShell Jobs if they take too long

I have a relatively long/complex script that blindly runs a batch of commands against several devices (as jobs). Once in awhile, a couple of these jobs continue to run indefinitely. I’ve added a Wait-Job -Timeout command to my script (see below) in order to force-stop jobs that are taking too long to run.

I’d like to change the output for these hung jobs to read “This device is busy or unstable”. How can I do this? I'm guessing I need to add something to the tail-end of the pipeline (in the last line of code below).

$Jobs += Get-Job
$jobs | Wait-Job -Timeout 5 | out-null  

Get-Job | ? {$_.State -eq 'Running'} | Stop-Job -PassThru 


Solution 1:[1]

One way is to iterate over the jobs that are currently running and write your message for each one:

$Jobs += Get-Job
$Jobs | Wait-Job -Timeout 5 | Out-Null

Get-Job | ? { $_.State -eq 'Running' } | Stop-Job -PassThru | % { Write-Host "This device is busy or unstable" }

You can also add info from the jobs that are being stopped, like the job ID for example:

Get-Job | ? { $_.State -eq 'Running' } | Stop-Job -PassThru | % { Write-Host "This device is busy or unstable: $($_.Id)" }

UPDATE: You can use a hashtable to store the job IDs that were "force stopped". Then iterate through the Jobs using Receive-Job to get the output, and check if the job is in the ForceStoppedIds table. If it is write your custom message, otherwise just output the message. Here's a simple test I ran.

Start-Job -ScriptBlock { Write-Output "Starting 1."; Start-Sleep -Seconds 3; Write-Output "1 complete."; } | Out-Null
Start-Job -ScriptBlock { Write-Output "Starting 2."; Start-Sleep -Seconds 60; Write-Output "2 complete."; } | Out-Null
Start-Job -ScriptBlock { Write-Output "Starting 3."; Start-Sleep -Seconds 2; Write-Output "3 complete."; } | Out-Null
$Jobs += Get-Job
$Jobs | Wait-Job -Timeout 5 | Out-Null
$ForceStoppedIds = @{}
$Jobs | ? { $_.State -eq 'Running' } | Stop-Job -PassThru | % { $ForceStoppedIds[$_.Id] = $true }
foreach ($job in $Jobs) {
    $jobOutput = Receive-Job $job -Wait
    if ($ForceStoppedIds.Contains($job.Id)) {
        Write-Host "Custom message about stopped job: $($jobOutput)"
    }
    else {
        Write-Host $jobOutput
    }
}

One thing to be cautious of is how jobs output information (ie. Write-Host, Write-Output, return, etc.). If you're not getting the results you expect, double check the job's ScriptBlock to see how the information is being written/returned/etc. I'm sure there are much more elegant ways of doing this, but hopefully this will help.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1