11

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

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

### 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次。但相反,我没有在控制台中显示“开始工作”/“结束工作”。

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

{术语“DisplayMessage”未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。检查名称的拼写,或者如果包含路径,请验证路径是否正确,然后重试。}

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

4

3 回答 3

8

在后台作业中包含本地函数的简单方法:

$init=[scriptblock]::create(@"
function DoWork {$function:DoWork}
"@)

Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null
于 2013-03-20T10:27:19.843 回答
6

后台作业在单独的进程中运行,因此您创建的作业Start-Job不能与函数交互,除非您将它们包含在$scriptblock.

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

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

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

无标题1.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
于 2013-03-20T10:15:42.827 回答
0

我让它按如下方式工作;

function CreateTable {
    $table = "" | select ServerName, ExitCode, ProcessID, StartMode, State, Status, Comment
    $table
}

$init=[scriptblock]::create(@"
  function CreateTable {$function:createtable}
"@)
于 2019-07-12T16:38:30.810 回答