开发者

How do I Start a job of a function i just defined?

How do I Start a job of a function i just defined?

function FOO { write-host "HEY" } Start-Job -ScriptBlock { FOO } |
R开发者_如何学Goeceive-Job

Receive-Job: The term 'FOO' is not recognized as the name of cmdlet,
function ,script file or operable program.

What do I do? Thanks.


As @Shay points out, FOO needs to be defined for the job. Another way to do this is to use the -InitializationScript parameter to prepare the session.

For your example:

$functions = {
    function FOO { write-host "HEY" }
}

Start-Job -InitializationScript $functions -ScriptBlock {FOO}|
    Wait-Job| Receive-Job

This can be useful if you want to use the same functions for different jobs.


@Rynant's suggestion of InitializationScript is great

I thought the purpose of (script) blocks is so that you can pass them around. So depending on how you are doing it, I would say go for:

$FOO = {write-host "HEY"}

Start-Job -ScriptBlock $FOO | wait-job |Receive-Job

Of course you can parameterize script blocks as well:

$foo = {param($bar) write-host $bar}

Start-Job -ScriptBlock $foo -ArgumentList "HEY" | wait-job | receive-job


It worked for me as:

Start-Job -ScriptBlock ${Function:FOO}


An improvement to @Rynant's answer:

You can define the function as normal in the main body of your script:

Function FOO 
{ 
  Write-Host "HEY" 
} 

and then recycle this definition within a scriptblock:

$export_functions = [scriptblock]::Create(@"
  Function Foo { $function:FOO }
"@)

(makes more sense if you have a substantial function body) and then pass them to Start-Job as above:

Start-Job -ScriptBlock {FOO} -InitializationScript $export_functions| Wait-Job | Receive-Job

I like this way, as it is easier to debug jobs by running them locally under the debugger.


The function needs to be inside the scriptblock:

Start-Job -ScriptBlock { function FOO { write-host "HEY" } ; FOO } | Wait-Job | Receive-Job


A slightly different take. A function is just a scriptblock assigned to a variable. Oh, it has to be a threadjob. It can't be foreach-object -parallel.

$func = { 'hi' } # or
function hi { 'hi' }; $func = $function:hi

start-threadjob { & $using:func } | receive-job -auto -wait

hi


@Ben Power's comment under the accepted answer was my concern also, so I googled how to get function definitions, and I found Get-Command - though this gets only the function body. But it can be used also if the function is coming from elsewhere, like a dot-sourced file. So I came up with the following (hold my naming convention :)), the idea is to re-build the function definitions delimited by newlines:

Filter Greeting {param ([string]$Greeting) return $Greeting}
Filter FullName {param ([string]$FirstName, [string]$LastName) return $FirstName + " " + $LastName}
$ScriptText = ""
$ScriptText += "Filter Greeting {" + (Get-Command Greeting).Definition + "}`n"
$ScriptText += "Filter FullName {" + (Get-Command FullName).Definition + "}`n"
$Job = Start-Job `
            -InitializationScript $([ScriptBlock]::Create($ScriptText)) `
            -ScriptBlock {(Greeting -Greeting "Hello") + " " + (FullName -FirstName "PowerShell" -LastName "Programmer")}
$Result = $Job | Wait-Job | Receive-Job
$Result
$Job | Remove-Job


As long as the function passed to the InitializationScript param on Start-Job isn't large Rynant's answer will work, but if the function is large you may run into the below error.

[localhost] There is an error launching the background process. Error reported: The filename or extension is too long"

Capturing the function's definition and then using Invoke-Expression on it in the ScriptBlock is a better alternative.

function Get-Foo {
    param
    (
        [string]$output
    )

    Write-Output $output
}

$getFooFunc = $(Get-Command Get-Foo).Definition

Start-Job -ScriptBlock {
    Invoke-Expression "function Get-Foo {$using:getFooFunc}"
    Get-Foo -output "bar"
}

Get-Job | Receive-Job

PS C:\Users\rohopkin> Get-Job | Receive-Job
bar
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜