2

I have some dificulties with using an User Define Function inside the ForEach-Object when the Parallel is active:

function Add([Int]$number) {
    Write-Output "Run for $number"
}

1..4 | ForEach-Object -Parallel {
    Add -number $_
}

I get an error message:

The term 'Add' is not recognized as a name of a cmdlet, function, script file, or executable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

I believe it is a common proactivity use an UDF inside foreach loop. Do you have any workaround for that? The $using: is applicable only for variable, not function, right?

skolukmar
  • 185
  • 1
  • 9
  • 2
    As for `$using:` only working with variables: indeed, although you can - in principle - use _namespace variable notation_ to get a function's _body_, as a _script block_: `$using:function:Add`. However, while that works with `Start-Job` and `Start-ThreadJob`, it is explicitly prevent with `ForEach-Object -Parallel` (and, based on the rationale for that, should probably be prevented in `Start-ThreadJob` too). See the linked duplicate for details. – mklement0 Feb 25 '22 at 16:28

1 Answers1

3

You could either define the function in the scope of the parallel block (Runsapce):

1..4 | ForEach-Object -Parallel {
    function Add([Int]$number) {
        Write-Output "Run for $number"
    }
    
    Add -number $_
}

Or store the definition of your function and then pass it to the scope of your parallel block and dot source it:

function Add([Int]$number) {
    Write-Output "Run for $number"
}

$def = "function Add { ${function:Add} }"

1..4 | ForEach-Object -Parallel {
    . ([scriptblock]::Create($using:def))
    Add -number $_
}

Linked helpful answer from mklement0 shows a much more cleaner alternative to define the function in the parallel scope. Definitely recommend this method, easier to read and understand:

function Add([Int]$number) {
    Write-Output "Run for $number"
}

$def = ${function:Add}.ToString()

1..4 | ForEach-Object -Parallel {
    $function:Add = $using:Def
    Add -number $_
}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 1
    @mklement0 actually a lot cleaner the approach on the linked answer, wasn't aware of it. Thanks – Santiago Squarzon Feb 25 '22 at 17:15
  • 1
    Including the function definition directly in the `-Parallel` script block is a pragmatic solution, though not always a (clean) option. – mklement0 Feb 25 '22 at 17:19
  • 1
    I have always used this method and always wondered if there was something cleaner, guess I learnt something new today, as per usual with your answers :P @mklement0 – Santiago Squarzon Feb 25 '22 at 17:25