I'm trying to make jobs with powershell but they get stuck for a reason.
Untitled2.ps1:
$A = "papa"
$B = "mama"
Start-Job -ScriptBlock { MainHA $args[0] $args[1] } -ArgumentList #($A, $B) -InitializationScript { . "C:\Tools\Untitled3.ps1" }
While (Get-Job -State "Running")
{
write-host Still working...
Get-Job | Receive-Job
Start-Sleep 1
}
Get-Job | Receive-Job
Remove-Job *
Untitled3.ps1:
Function MainHA ($x, $y)
{
write-host $x, $y
}
Any idea?
Are there any other jobs running?
One way to wait for the specific job you are starting is to store the job you start in a variable.
$job = Start-Job -ScriptBlock { MainHA $args[0] $args[1] } -ArgumentList #($A, $B) -InitializationScript { . "C:\Tools\Untitled3.ps1" }
While ($job.State -eq "Running") {
write-host Still working...
Receive-Job $job
Start-Sleep 1
}
Receive-Job $job
Remove-Job $job
Related
I would like to force-terminate/timeout a PowerShell Invoke-Command remote session after 20 minutes regardless of whether it's busy or idle.
How do I achieve this? Thanks.
Something like :
$job = Start-Job -ScriptBlock { <# Invoke-Command #> }
$job | Wait-Job -Timeout ( 20 * 60 ) | Remove-Job
PS> invoke_command_responsive -Machine pv3040 -Cmd "get-location"
function invoke_command_responsive {
param(
[string]$Machine,
[string]$Cmd
)
$start_time = Get-Date
$start_dir = better_resolve_path(".")
$watchdog = 8 #seconds
[ScriptBlock]$sb = [ScriptBlock]::Create($opt_cmd)
$j = Start-Job -ScriptBlock {
set-location $using:start_dir | out-null
[ScriptBlock]$sb = [ScriptBlock]::Create($using:cmd)
invoke-command -Computer $using:Machine -ScriptBlock:$sb | out-host
}
# Wait for Job to Complete or TIMEOUT!
while($true) {
if ($j.HasMoreData) {
Receive-Job $j
Start-Sleep -Milliseconds 50
}
$current = Get-Date
$time_span = $current - $start_time
if ($time_span.TotalSeconds -gt $watchdog) {
write-host "TIMEOUT!"
Stop-Job $j
break
}
if (-not $j.HasMoreData -and $j.State -ne 'Running') {
write-host "Finished"
break
}
}
Remove-Job $j
}
function better_resolve_path {
param([string]$path)
$pathfix = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
return $pathfix
}
I have this as part of a script
$timeoutSeconds = $timeoutMinutes * 60
$job = Start-Job -ScriptBlock $block -ArgumentList #($environment, $filter)
Wait-Job $job -Timeout $timeoutSeconds
Stop-Job $job
Remove-Job $job
And I would like to raise an error (is part of an octopus deploy step) if it's timed out
Thanks
How about something like this:
Wait-Job $job -Timeout $timeoutSeconds
if ($job.state -eq 'Running') {
Write-Error "Job timed out but did not complete."
}
$jobResults = Receive-Job $job
$jobResults
Stop-Job $job
Remove-Job $job
I have written a ScriptBlock that POSTs file uploads to a custom http server and saves the response to disk. It is intended to be used for testing said server and in addition to checking the correct response I'd like to run it in parallel to load the server.
The code in the ScriptBlock was built as separate ps1 script and works. I have then transplanted it into a framework-script using Start-Job for managing the jobs and eventually presenting overall results.
Unfortunately it does not work after that transplantation.
The symptom is that using Start-Job the job never finishes. For testing I have switched to Invoke-Command. A simple Invoke-Command works, InvokeCommand -AsJob does not.
Works:
Invoke-Command -Scriptblock $ScriptBlock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
Do not work:
Invoke-Command -AsJob -Computer localhost -Scriptblock $ScriptBlock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
Start-Job -Name $Name -ScriptBlock $Scriptblock -ArgumentList $Name,"C:\Projects\DCA\CCIT\ServiceBroker4\Java\eclipse-workspace\XFAWorker\testdata","P7-T"
The ScriptBlock being used is rather longish. It starts off by declaring Parameters:
$Scriptblock = {
Param (
[string]$JobName,
[string]$path,
[string]$BASEJOBNAME
)
And further down uses HttpWebRequest and a few other .NET classes:
$url = "http://localhost:8081/fillandflatten"
[System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::create($url)
...
$xfabuffer = [System.IO.File]::ReadAllBytes("$path\$BASEJOBNAME.xml")
...
$header = "--$boundary`r`nContent-Disposition: form-data; name=`"xfa`"; filename=`"xfa`"`r`nContent-Type: text/xml`r`n`r`n"
$buffer = [Text.Encoding]::ascii.getbytes($header)
...
[System.Net.httpWebResponse] $res = $req.getResponse()
As described when using Start-Job or Invoke-Command -AsJob the child job started simply remains in Running state forever.
What can cause this behaviour? What can be used to debug it? If there is some error can I force the child-job to terminate and tell me what it does not like?
I am using PowerShell 2.0 on Windows XP.
In the framework I came up with I do the Start-Job (currently just one of them, but I plan to ramp up that number for stress-testing). Then I have a loop waiting for them all to terminate:
do {
$Jobs = #(Get-Job | Where { $_.State -eq "Running" -and $_.Name.StartsWith($BASEJOBNAME) });
$n = $Jobs.Count
if ($n -gt 0)
{
Log -message "Waiting for $n jobs to finish..."
Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } | Format-Table -AutoSize -Property Id,Name,State,HasMoreData,Location
start-Sleep -Seconds 3
}
} until ($n -eq 0)
This produces output of the form:
2014-08-01 18:58:52 - Waiting for 1 jobs to finish...
Id Name State HasMoreData Location
-- ---- ----- ----------- --------
2 XFA001 Running True localhost
Forever.
A full minimal test-case is:
# Code for the Jobs:
$Scriptblock = {
[System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::create("http://locahost/index.html")
return "done"
}
# Start a job. Three variants, The Job-based ones hang.
# Invoke-Command -Scriptblock $ScriptBlock
#Invoke-Command -AsJob -Computer localhost -Scriptblock $ScriptBlock
$Job = Start-Job -Name $Name -ScriptBlock $Scriptblock
## The rest of the code is only applicable for the Start-Job-Variant
# Wait for all Jobs to finish
do {
$Jobs = #(Get-Job | Where { $_.State -eq "Running" });
$n = $Jobs.Count
if ($n -gt 0)
{
Write-Host "Waiting for $n jobs to finish..."
Get-Job | Format-Table -AutoSize -Property Id,Name,State,HasMoreData
Start-Sleep -Seconds 3
}
} until ($n -eq 0)
# Get output from all jobs
$Data = ForEach ($Job in (#(Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } ))) {
Receive-Job $Job
}
# Clean out all jobs
ForEach ($Job in (#(Get-Job | Where { $_.Name.StartsWith($BASEJOBNAME) } ))) {
Remove-Job $Job
}
# Dump output
Write-Host "Output data:"
$Data | Format-Table
Write-Host ""
This hangs for me. If I comment out the line creating the WebRequest object it works.
Thank you.
When you run Get-Job , does the job's "HasMoreData" properties is "True" ?
If yes, check the output of the job :
Receive-Job <JobName or JobID> -Keep
What is the PowerShell v2.0 equivalent to this v3.0 code snippt:
for($i=0; $i -lt $num_jobs; $i++) {
Write-Host -ForegroundColor Darkgreen "[i] Job" $i "starting..."
Start-Job -ScriptBlock $insert_data -ArgumentList 'host', 'user', 'pass', 'db', $i, $log_out[$i] | Out-Null;
}
get-job | receive-job -AutoRemoveJob -Wait
I tried the following without luck
for($i=0; $i -lt $num_jobs; $i++) {
Write-Host -ForegroundColor Darkgreen "[i] Job" $i "starting..."
Start-Job -ScriptBlock $insert_data -ArgumentList 'host', 'user', 'pass', 'db', $i, $log_out[$i] | Out-Null;
}
get-job | receive-job -Wait
get-job | remove-job
It fails on PowerShell v2.0 with:
Remove-Job : The command cannot remove the job with the 3 session identifier be
cause the job is not finished. To remove the job, first stop the job, or use th
e Force parameter.
The best I can come up with for V2 is:
Get-Job | % {while ($_.HasMoreData) { Receive-Job $_; Sleep 1 }; Remove-Job $_}
Note: I recommend using the Get-Job -Id parameter in the event you have more than one job running. Also, a 1 second sleep might be a bit long, you could tweak that to say 250 millisecs.
For multiple jobs, you could do this:
while ($jobs = Get-Job) { $jobs | %{if ($_.HasMoreData) {Receive-Job $_} else {Remove-Job $_}}; Sleep 1};
try this in two distinct lines:
get-job | wait-job | receive-job
get-job | remove-job
My Powershell code doesn't evaluate the $agent variable:
foreach ($agent in $agentcomputers) {
Write-Output 'Starting agent on '$agent
# psexc to start the agent
Start-Job -ScriptBlock {& psexec $agent c:\grinder\examples\startAgent.cmd}
}
This link is similar to my problem, except I'm not calling an external Powershell script.
I tried adding that in, using $args[0] for $agent, and adding the -ArgumentList parameters, but that didn't work.
Edits/Replies
$agentcomputers is just a list of computer names - each on its own line:
$agentcomputers = Get-Content c:\grinder-dist\agent-computers.txt
I have also tried this - and $args[0] doesn't evaluate:
Start-Job -ScriptBlock {& psexec $args[0] c:\grinder\examples\startAgent.cmd} -ArgumentList #($agent)
Here are 3 different ways I would do it.
First, all aligned and pretty.
$agents = Get-Content c:\grinder-dist\agent-computers.txt
$jobs = {
Param($agent)
write-host "Starting agent on" $agent
& psexec \\$agent c:\grinder\examples\startAgent.cmd
}
foreach($agent in $agents) {
Start-Job -ScriptBlock $jobs -argumentlist $agent | Out-Null
}
Get-Job | Wait-Job | Receive-Job
Or you could just put it all on one line without creating any variables.
(Get-Content c:\grinder-dist\agent-computers.txt) | %{ Start-Job -ScriptBlock { param($_) write-host "Starting agent on" $_; & psexec \\$_ c:\grinder\examples\startAgent.cmd } -argumentlist $_ | Out-Null }
Get-Job | Wait-Job | Receive-Job
And in this final example, you could manage how many threads are run concurrently by doing it this way.
$MaxThreads = 5
$agents = Get-Content c:\grinder-dist\agent-computers.txt
$jobs = {
Param($agent)
write-host "Starting agent on" $agent
& psexec \\$agent c:\grinder\examples\startAgent.cmd
}
foreach($agent in $agents) {
Start-Job -ScriptBlock $jobs -argumentlist $agent | Out-Null
While($(Get-Job -State 'Running').Count -ge $MaxThreads) {
sleep 10
}
Get-Job | Wait-Job | Receive-Job
}
Here is the solution. As Andy said, I needed to use $args array with the -ArgumentList parameter. This other thread was helpful: Powershell: passing parameters to a job
foreach($agent in $agentcomputers){
$agentslash = "\\"+$agent
$args = ($agentslash,"c:\grinder\examples\startAgent.cmd")
Write-Output 'Starting agent on '$agent
#psexc to start the agent
$ScriptBlock = {& 'psexec' #args }
Start-Job -ScriptBlock $ScriptBlock -ArgumentList $args
}