如何在 Start-Job 的脚本中调用 powershell 函数? [英] How to call a powershell function within the script from Start-Job?

查看:120
本文介绍了如何在 Start-Job 的脚本中调用 powershell 函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到了这个问题这个问题,但找不到我的问题的解决方案.

I saw this question and this question but couldn't find solution to my problem.

情况是这样的:我在脚本 (.ps1) 文件中有两个函数 DoWork 和 DisplayMessage.代码如下:

So here is the situation: I have two functions DoWork and DisplayMessage in a script (.ps1) file. Here is the code:

### START OF SCRIPT ###
function DoWork
{
  $event = Register-EngineEvent -SourceIdentifier NewMessage -Action {
      DisplayMessage($event.MessageData)
  }

  $scriptBlock =  {

    Register-EngineEvent -SourceIdentifier NewMessage -Forward

    $message = "Starting work"
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message

    ### DO SOME WORK HERE ###

    $message = "Ending work"
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message

    Unregister-Event -SourceIdentifier NewMessage
  }

  DisplayMessage("Processing Starts")

  $array = @(1,2,3)
  foreach ($a in $array)
  {
      Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array | Out-Null
  }

  #$jobs = Get-Job -Name "DoActualWork"
  While (Get-Job -Name "DoActualWork" | where { $_.State -eq "Running" } )
  {
      Start-Sleep 1
  }

  DisplayMessage("Processing Ends")

  Get-Job -Name "DoActualWork" | Receive-Job
}

function DisplayMessage([string]$message)
{
    Write-Host $message -ForegroundColor Red
}

DoWork

### END OF SCRIPT ###

我正在创建 3 个后台作业(使用带有 3 个元素的 $array)并使用事件将消息从后台作业传递到主机.我希望powershell主机显示处理开始"和处理结束"1次,开始工作"和结束工作"各3次.但相反,我没有在控制台中显示开始工作"/结束工作".

I am creating 3 background jobs (using $array with 3 elements) and using event to pass messages from background jobs to the host. I would expect the powershell host to display "Processing Starts" and "Processing Ends" 1 time and "Starting work" and "Ending work" 3 times each. But instead I am not getting "Starting work"/"Ending work" displayed in console.

事件在 powershell 中也被视为作业,因此当我执行 Get-Job 时,我可以看到与事件作业相关的以下错误:

Event is also treated as a job in powershell, so when I do Get-Job, I can see following error associated with the event job:

{术语DisplayMessage"未被识别为 cmdlet 的名称,函数、脚本文件或可运行的程序.检查拼写名称,或者如果包含路径,请验证路径是否正确并且再试一次.}

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

我的问题:我们如何重用(或引用)在我调用 Start-Job 的同一脚本中定义的函数(在我的例子中是 DisplayMessage)?是否可以?我知道我们可以使用 -InitializationScript 将函数/模块传递给 Start-Job,但我不想写两次 DisplayMessage 函数,一个在脚本中,另一个在 InitializationScript 中.

My question: How can we reuse (or reference) a function (DisplayMessage in my case) defined in the same script from where I am calling Start-Job? Is it possible? I know we can use -InitializationScript to pass functions/modules to Start-Job, but I do not want to write DisplayMessage function twice, one in script and another in InitializationScript.

推荐答案

后台作业在单独的进程中运行,因此您使用 Start-Job 创建的作业无法与函数交互,除非您包括它们在 $scriptblock 中.

Background jobs are run in a seperate process, so the jobs you create with Start-Job can not interact with functions unless you include them in the $scriptblock.

即使您在 $scripblock 中包含该函数,Write-Host 也不会将其内容输出到控制台,直到您使用 Get-Job |Receive-Job 接收作业结果.

Even if you included the function in the $scripblock, Write-Host would not output it's content to the console until you used Get-Job | Receive-Job to recieve the jobs result.

EDIT 问题是您的 DisplayMessage 函数在本地脚本范围内,而您的事件处理程序在不同的父范围内运行(例如 global 是会话范围),所以它找不到你的函数.如果您在全局范围内创建函数并从全局范围调用它,它将起作用.

EDIT The problem is that your DisplayMessage function is in a local script-scope while your eventhandler runs in a different parent scope(like global which is the session scope), so it can't find your function. If you create the function in the global scope and call it from the global scope, it will work.

我现在已经修改了您的脚本来执行此操作.我还修改了 scriptblock 并在脚本完成后取消注册事件,这样在多次运行脚本时不会收到 10x 消息:-)

I've modified your script to do this now. I've also modified to scriptblock and unregistered the events when the script is done so you won't get 10x messages when you run the script multiple times :-)

Untitled1.ps1

Untitled1.ps1

function DoWork
{
  $event = Register-EngineEvent -SourceIdentifier NewMessage -Action {
      global:DisplayMessage $event.MessageData
  }

  $scriptBlock =  {

    Register-EngineEvent -SourceIdentifier NewMessage -Forward

    $message = "Starting work $args"
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message

    ### DO SOME WORK HERE ###

    $message = "Ending work $args"
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message

    Unregister-Event -SourceIdentifier NewMessage
  }

  DisplayMessage("Processing Starts")

  $array = @(1,2,3)
  foreach ($a in $array)
  {
      Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $a | Out-Null
  }

  #$jobs = Get-Job -Name "DoActualWork"
  While (Get-Job -Name "DoActualWork" | where { $_.State -eq "Running" } )
  {
      Start-Sleep 1
  }

  DisplayMessage("Processing Ends")

  #Get-Job -Name "DoActualWork" | Receive-Job
}

function global:DisplayMessage([string]$message)
{
    Write-Host $message -ForegroundColor Red
}

DoWork

Get-EventSubscriber | Unregister-Event

测试

PS > .\Untitled1.ps1
Processing Starts
Starting work 1
Starting work 2
Ending work 1
Ending work 2
Starting work 3
Ending work 3
Processing Ends

这篇关于如何在 Start-Job 的脚本中调用 powershell 函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆