Reputation: 4853
I have a script that has some functions and then multiple jobs in the very same script that use those functions. When I start a new job they don't seem to be accessible in the [ScriptBlock]
that I have for my jobs.
Here's a minimal example demonstrating this:
# A simple test function
function Test([string] $string)
{
Write-Output "I'm a $string"
}
# My test job
[ScriptBlock] $test =
{
Test "test function"
}
# Start the test job
Start-Job -ScriptBlock $test -Name "Test" | Out-Null
# Wait for jobs to complete and print their output
@(Get-Job).ForEach({
Wait-Job -Job $_ |Out-Null
Receive-Job -Job $_ | Write-Host
})
# Remove the completed jobs
Remove-Job -State Completed
The error that I get in PowerShell ISE is:
The term 'Test' 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.
+ CategoryInfo : ObjectNotFound: (Test:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : localhost
Upvotes: 5
Views: 2559
Reputation: 1151
Just extending PetSerAl's answer, you can use Runspaces for this, if you want faster code and a little bit more organised. Check out this question: 39180266
So when you run something in different runspace, you need to import functions in both of them. So finished structure would look like:
Module: functions.ps1
- you store here functions to share with both scopes.Main script: script.ps1
- it's basically your script, with runspaces, but without functions from functions.ps1.And in beginning of your script.ps1, just simply call Import-module .\functions.ps1
, to get access to your functions. Remember that runscape has different scope, and in their scriptblock, you have to call import-module once again. Full example:
#file functions.ps1
function add($inp) {
return $inp + 2
}
#file script.ps1
Import-module .\functions.ps1 #or you can use "dot call": . .\function.ps1
Import-module .\invoke-parallel.ps1 #it's extern module
$argument = 10 #it may be any object, even your custom class
$results = $argument | Invoke-Parallel -ScriptBlock {
import-module .\functions.ps1 #you may have to use here absolute path, because in a new runspace PSScriptRoot may be different/undefined
return (add $_) # $_ is simply passed object from "parent" scope, in fact, the relationship between scopes is not child-parent
}
echo $result # it's 12
echo (add 5) # it's 7
Upvotes: 0
Reputation: 22122
Start-Job
run jobs in separate PowerShell processes. So that, jobs do not have access to session state of calling PowerShell session. You need to define functions, which get used by jobs, in every job. An easy way to do that without duplicating the code would be using of -InitializationScript
parameter, where all common functions can be defined.
$IS = {
function CommonFunction1 {
'Do something'
}
function CommonFunction2 {
'Do something else'
}
}
$SB1 = {
CommonFunction1
CommonFunction2
}
$SB2 = {
CommonFunction2
CommonFunction1
}
$Job1 = Start-Job -InitializationScript $IS -ScriptBlock $SB1
$Job2 = Start-Job -InitializationScript $IS -ScriptBlock $SB2
Receive-Job $Job1,$Job2 -Wait -AutoRemoveJob
Upvotes: 6