Guidelines

This site is for tech Q&A. Please keep your posts focused on the subject at hand.

Ask one question at a time. Don't conflate multiple problems into a single question.

Make sure to include all relevant information in your posts. Try to avoid linking to external sites.

Links to documentation are fine, but in addition you should also quote the relevant parts in your posts.

1 vote
9.5k views
9.5k views

I have a script which include two functions and I did put this two functions into a single function so that it would work parallelly when I'm running it. Now I want to run the whole script as one job so that I can run the script on multiple folders and see the output without confusion.

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [String]$currentPath
)
Function tfinit { Do something }
function changes { Do something}
function runjobs {
    Param ($path)
    set-location $path
    #remove blank workspace and space from the list
    $workspace = terraform workspace list
    $workspaces = @()
    $workspace | ForEach-Object {
        $a = $_ -replace "\*", ""
        $a = $a -replace ' ', ''
        $workspaces += $a
    }
    ### Do init
    tfinit -path $path
    ##### Do plan job
    foreach ($workspace in $workspaces) {
        if ($workspace -ne "") {
            changes -workspace $workspace -path $path 
        }
    }
    ### Get folder inside the folder to run
    $list = Get-ChildItem -Directory
    foreach ($folder in $list) {
        set-location $folder.fullname
        runjobs -path "$folder.fullname"
    }
}
runjobs -path $currentPath

I do want the function runJob to run as a Start-Job.

Start-Job -ScriptBlock {
    runJobs -path $args[0]
} -InitializationScript $allJobs  -ArgumentList "$currentPath" | Wait-Job | Receive-Job

When I tried to execute this it's running on a totally different folder. I want the Start-Job for the runJob function to work on the path that I'm giving to $currentPath.

in Scripting
edited by
by (5)
1 2
edit history
 
It's not quite clear to me what you mean by "run as one job." Do you want to run the script as a background job (via Start-Job)? Can you give an example of the output you expect compared to the output you currently get? Also, the recursive runjobs call in your script won't work the way you expect. PowerShell only does simple variable expansion in strings, so "$folder.fullname" does the same as $folder + ".fullname" (expand the variable $folder and append the string ".fullname").
 
Yes I do want the function runJob to run as a start-job.

'Start-Job -ScriptBlock { runJobs -path $args[0] } -InitializationScript $allJobs  -ArgumentList "$currentPath" | Wait-Job | Receive-Job'

when i tried to execute this its running on a totally different folder. I want the start-job for the runJob function to work on the path that I'm giving to the currentPath.

Please log in or register to answer this question.

2 Answers

0 votes

To invoke a function in a scriptblock run as a job you need to define the function (all functions invoked in the scriptblock) inside the context of the job. Otherwise you'll get an error like this:

ObjectNotFound: The term 'runjobs' 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.

To include the function definitions in the code run by the job you can either put them inside the invoked scriptblock or pass them as a separate scriptblock (parameter -InitializationScript). The latter runs the given initialization code once before the actual job starts, similar to how a BEGIN {} block in a function works.

So, if you want to invoke Start-Job from within the script the code in your script would have to look somewhat like this:

[CmdletBinding()]
Param(
    [Parameter(Mandatory = $true)]
    [String]$currentPath
)

Start-Job -ScriptBlock {
    function tfinit {
        #...
    }
    function changes {
        #...
    }
    function runjobs {
        Param($path)
        #...
    }

    runJobs -path $args[0]
} -ArgumentList "$currentPath" | Wait-Job | Receive-Job

or like this:

[CmdletBinding()]
Param(
    [Parameter(Mandatory = $true)]
    [String]$currentPath
)

$jobFunctions = {
    function tfinit {
        #...
    }
    function changes {
        #...
    }
    function runjobs {
        Param($path)
        #...
    }
}

Start-Job -InitializationScript $jobFunctions -ScriptBlock {
    runJobs -path $args[0]
} -ArgumentList "$currentPath" | Wait-Job | Receive-Job

However, Start-Job is not limited to running scriptblocks. It can also invoke scripts, so you could leave your script as it is, and run it as a job like this:

Start-Job -FilePath C:\path\to\your.ps1 -ArgumentList "some_path" |
    Wait-Job |
    Receive-Job

For example in a runner script like this (e.g. run_job.ps1):

[CmdletBinding()]
Param(
    [Parameter(Mandatory = $true)]
    [String]$Script,
    [Parameter(Mandatory = $true)]
    [String]$Path,
    [Parameter(Mandatory = $false)]
    [ScriptBlock]$InitializationScript = {}
)
Start-Job -FilePath $Script -InitializationScript $InitializationScript -ArgumentList $Path |
    Wait-Job |
    Receive-Job

that you invoke with your script and the working directory:

.\run_job.ps1 -Script 'C:\path\to\foo.ps1' -Path 'D:\your\path'

As for "its running on a totally different folder," you're invoking Set-Location $path at the beginning of runjobs(), so the working directory should be fine unless you're passing a relative path (foo\bar) rather than an absolute path (D:\foo\bar). If it isn't: please provide evidence (edit your question to do so).


edited by
by (125)
3 19 33
edit history
0 votes
 
    [CmdletBinding()]
Param(
    [Parameter(Mandatory = $true)]
    [String]$currentPath
)

$allJobs = {
        function tfinit {
            #...
        }
        function changes {
            #...
        }
        function runjobs {
            Param($path)
            #...
        }

    }
}

Start-Job -Initializationscript $allJobs  -ScriptBlock { runJobs -path $args[0] } -ArgumentList "$currentPath" 
by (5)
1 2
edit history
 
Is there a particular reason why you post and accept an answer of your own that basically copies my first suggestion (while adding a spurious trailing brace no less)? Whether the functions are defined via -InitializationScript or directly in the scriptblock makes no difference here.
 
I just wanted people to find the answer to this particular question easily and as you can see I altered the same code that i used for asking the question at the first place.  So i could say i basically copied my own code not yours. which is

'Start-Job -ScriptBlock {
    runJobs -path $args[0]
} -InitializationScript $allJobs  -ArgumentList "$currentPath" | Wait-Job | Receive-Job'
 
The answer to your question is "make function definitions part of the code run by the job." Whether that's done in the invoked scriptblock or in an additional initialization scriptblock is an irrelevant implementation detail. A code-only answer without explanation but with an obvious syntax error will hardly be useful to anyone.
...