如何从Start-Job中调用脚本中的powershell函数



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

那么情况是这样的:我在脚本(.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个后台作业(使用$array与3个元素),并使用事件从后台作业传递消息到主机。我希望powershell主机显示"Processing Starts"one_answers"Processing Ends"各1次,"Starting work"one_answers"Ending work"各3次。但是,我没有得到"开始工作"/"结束工作"显示在控制台。

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

{术语"DisplayMessage"未被识别为cmlet的名称,函数、脚本文件或可操作程序。检查单词的拼写名称,或者如果包含路径,则验证路径是否正确再试一次。}

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

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

$init=[scriptblock]::create(@"
function DoWork {$function:DoWork}
"@)
Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null

后台作业在一个单独的进程中运行,所以你用Start-Job创建的作业不能与函数交互,除非你把它们包含在$scriptblock中。

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

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

我已经修改了你的脚本,现在这样做。我还修改了scriptblock,并在脚本完成时取消了事件的注册,这样当您多次运行脚本时,您就不会得到10x消息:-)

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

我让它工作如下;

function CreateTable {
    $table = "" | select ServerName, ExitCode, ProcessID, StartMode, State, Status, Comment
    $table
}
$init=[scriptblock]::create(@"
  function CreateTable {$function:createtable}
"@)

相关内容

  • 没有找到相关文章

最新更新