Search code examples
multithreadingpowershellpowershell-3.0jobs

Passing relative paths of scripts to powershell jobs


I have functions in separate files I need to run as jobs in one main file.

I need to be able to pass these functions arguments.

Right now my problem is figuring out how to pass the path of the function files to the jobs in a way that is not completely awful.

I need to have the functions defined at the top of the file for readability (just having a static comment that says "script uses somefunc.ps1" is not adequate)

I also need to refer to the scripts relative path (they will all be in the same folder).

Right now I am using env: to store the path of the scripts, but doing this I need to refer to the script in like 5 places!

This is what I have:

testJobsMain.ps1:

#Store path of functions in env so jobs can find them
$env:func1 = "$PSScriptRoot\func1.ps1"
$env:func2 = "$PSScriptRoot\func2.ps1"

$arrOutput = @()
$Jobs = @()
foreach($i in ('aaa','bbb','ccc') ) {

    $Import = {. $env:func1}
    $Execute = {func1 -myArg $Using:i}

    $Jobs += Start-Job -InitializationScript $Import -ScriptBlock $Execute
}

$JobsOutput = $Jobs | Wait-Job | Receive-Job
$JobsOutput

$Jobs | Remove-Job
#Clean up env
Remove-Item env:\func1
$arrOutput

func1.ps1

function func1( $myArg ) { write-output $myArg }

func2.ps1

function func2( $blah ) { write-output $blah }

Solution

  • You can simply make array of paths, and then pass one of paths/all of them in -ArgumentList param from Start-Job:

    #func1.ps1
    function add($inp) {
        return $inp + 1
    }
    
    #func2.ps1
    function add($inp) {
        return $inp + 2
    }
    
    $paths = "$PSScriptRoot\func1.ps1", "$PSScriptRoot\func2.ps1"
    
    $i = 0
    ForEach($singlePath in $paths) {
        $Execute = {
            Param(
                [Parameter(Mandatory=$True, Position=1)]
                 [String]$path
            )
            Import-Module $path
            return add 1
        }
        Start-Job -Name "Job$i" -ScriptBlock $Execute -ArgumentList $singlePath
        $i++
    }
    
    for ($i = 0; $i -lt 2; $i++) {
        Wait-Job "Job$i"
        [int]$result = Receive-Job "Job$i"
    }
    

    You can skip all those $i iterators with names, Powershell will name jobs automatically, and easly predictable: Job1, Job2.. So it would make code a lot prettier.