13

I have the following code.

function createZip
{
Param ([String]$source, [String]$zipfile)
Process { echo "zip: $source`n     --> $zipfile" }
}

try {
    Start-Job -ScriptBlock { createZip "abd" "acd" } 
}
catch {
    $_ | fl * -force
}
Get-Job | Wait-Job 
Get-Job | receive-job 
Get-Job | Remove-Job 

However, the script returns the following error.

Id              Name            State      HasMoreData     Location             Command                  
--              ----            -----      -----------     --------             -------                  
309             Job309          Running    True            localhost            createZip "a...
309             Job309          Failed     False           localhost            createZip "a...
Receive-Job : The term 'createZip' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:17 char:22
+ Get-Job | receive-job <<<<  
    + CategoryInfo          : ObjectNotFound: (function:createZip:String) [Receive-Job], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

It seems the function name cannot be recognized inside the script block of start-job. I tried function:createZip too.

ca9163d9
  • 27,283
  • 64
  • 210
  • 413

2 Answers2

16

Start-Job actually spins up another instance of PowerShell.exe which doesn't have your createZip function. You need to include it all in a script block:

$createZip = {
    param ([String]$source, [String]$zipfile)
    Process { echo "zip: $source`n     --> $zipfile" }
}

Start-Job -ScriptBlock $createZip  -ArgumentList "abd", "acd"

An example returning an error message from the background job:

$createZip = {
    param ([String] $source, [String] $zipfile)

    $output = & zip.exe $source $zipfile 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw $output
    }
}

$job = Start-Job -ScriptBlock $createZip -ArgumentList "abd", "acd"
$job | Wait-Job | Receive-Job

Also note that by using a throw the job object State will be "Failed" so you can get only the jobs which failed: Get-Job -State Failed.

Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • Thanks. It seems the exception raised in another instance of PowerShell cannot be captured. What's the best way to capture the exception? – ca9163d9 Jan 05 '12 at 22:48
  • 1
    Thanks for this - I had a similar question and now I'm golden! – marceljg Apr 18 '12 at 15:20
7

If you are still new to using start-job and receive-job, and want to debug your function more easily, try this form:

   $createZip = { 
   function createzipFunc {
     param ([String]$source, [String]$zipfile)
     Process { echo "zip: $source`n     --> $zipfile" }
     }
     #other funcs and constants here if wanted...
   }
   # some secret sauce, this defines the function(s) for us as locals
   invoke-expression $createzip

   #now test it out without any job plumbing to confuse you
   createzipFunc "abd" "acd"

   # once debugged, unfortunately this makes calling the function from the job  
   # slightly harder, but here goes...
   Start-Job -initializationScript $createZip -scriptblock {param($a,$b) `
createzipFunc $a $b } -ArgumentList "abc","def"

All not made simpler by the fact I did not define my function as a simple filter as you have, but which I did because I wanted to pass a number of functions into my Job in the end.

Sorry for digging this thread out, but it solved my problem too and so elegantly at that. And so I just had to add this little bit of sauce which I had written while debugging my powershell job.

  • 2
    I like this approach. One thing to add is sometimes `Invoke-Expression` will complain `Invoke-Expression : Cannot evaluate parameter 'Command' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.`. I found that if you pass the variable as a string `invoke-expression "$createzip"` solves that issue. – Adarsha Apr 05 '17 at 14:29