function F2([String]$var2)
{
.........
.........
}
function F1([String]$var1)
{
.........
F2 $
What a Start-Job does is to create a separate thread in the backend, initial another default Powershell session in the thread and run the commands passed in with the parameter -ScriptBlock.
The session in the new thread is a fresh session, it doesn't have any non-default module, variable or function loaded in. It has its own variable scoping and life cycle. So I'm not sure if it's possible to let it use the function defined in the default session, but at least it's not easy as I can see.
One workaround I can provide is to use the parameter -InitializationScript to define the function F2 like below
function F1([String]$var1)
{
.........
F2 $var2
.........
}
..................
..................
while ($i -le $count)
{
F1 "dir$i"
Start-Job -ScriptBlock ${function:F1} -ArgumentList @($i) -InitializationScript {function F2([String]$var2){.........}}
$i = $i + 1
}
Alternatively, if you have to define multiple functions like F1 that needs to call F2 and want to somehow reuse the code of F2's definition. We can create a ScriptBlock object for F2
$InitializationScript = [ScriptBlock]::Create('function F2([String]$var2){.........}')
function F1([String]$var1)
{
.........
F2 $var2
.........
}
..................
..................
while ($i -le $count)
{
F1 "dir$i"
Start-Job -ScriptBlock ${function:F1} -ArgumentList @($i) -InitializationScript $InitializationScript
$i = $i + 1
}
Best regards,
Dong
This is practically the same answer as Dong Mao's, which was here first, but I wrote it on the duplicate without realizing this was here.
Anyway, this method (using modules) may better address the scaling and duplication of code issue.
It's not so much that F2
is out of scope, it's that it doesn't even exist in the job.
Jobs are run as separate processes, so nothing is included from the calling process unless you explicitly give it.
When you reference ${function:F1}
you're using the PSDrive form of variable. In essence, it is the equivalent of Get-Content -LiteralPath Function:\F1
.
If you look at what is returned, you'll see that 1) it's a [ScriptBlock]
(which is exactly the type you need for Start-Job
's -ScriptBlock
parameter), but you'll also see that it is the function's contents; it doesn't even include the name.
This means that in the job you're directly executing the body of the function but you are not actually calling a function.
You could work around this re-generating the function definition part, defining the functions, or just defining F2
over again in the script block. This will start to get messy quickly.
My recommendation is to break your functions out into a module that is available on the system, then inside the script block, just call Import-Module MyFunctions
and they will be available.
If you don't want to go through that process of having separate files, and want to keep it contained, you can create a module on the fly. Just define your functions as a module in your script, and if you like, do it again in the job. Something like this:
$moduleDefinition = {
function F2([String]$var2)
{
"~$var2~"
}
function F1([String]$var1)
{
F2 $var1
}
}
# this makes the module loaded in this context
New-Module -Name MyFunctions -ScriptBlock $moduleDefinition
while ($i -le $count)
{
F1 "dir$i"
Start-Job -ScriptBlock {
param([String]$jobArg)
$modDef = [ScriptBlock]::Create($Using:moduleDefinition)
New-Module -Name MyFunctions -ScriptBlock $modDef
F1 $jobArg
} -ArgumentList @($i)
$i = $i + 1
}