Как вызвать функцию powershell в скрипте из Start-Job?

Я видел этот вопрос и этот вопрос, но не смог найти решение моей проблемы.

Итак, вот ситуация: У меня есть две функции DoWork и DisplayMessage в скрипте (.ps1) файл. Вот код:

### 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 отобразит "запуск обработки" и " обработка Заканчивается "1 раз и "начало работы" и "окончание работы" 3 раза каждый. Но вместо этого я не получаю "начало работы"/"завершение работы", отображаемое в консоли.

событие также рассматривается как задание в powershell, поэтому, когда я делаю Get-Job, я вижу следующую ошибку, связанную с заданием события:

{термин "DisplayMessage" не распознается как имя командлета, функции, файла скрипта или выполняемой программы. Проверьте правильность имени, или если путь был включен, проверьте правильность пути и попробовать еще раз.}

мой вопрос: как мы можем повторно использовать (или ссылаться) функцию (DisplayMessage в моем случае), определенную в том же скрипте, откуда я вызываю Start-Job? Возможно ли это? Я знаю, что мы можем использовать-InitializationScript для передачи функций/модулей в Start-Job, но я не хочу писать функцию DisplayMessage дважды, один в скрипте, а другой в InitializationScript.

2 ответов


фоновые задания выполняются в отдельном процессе, поэтому задания, которые вы создаете с помощью Start-Job не может взаимодействовать с функциями, если вы не включаете их в $scriptblock.

даже если вы включили функцию $scripblock, Write-Host не будет выводить его содержимое на консоль, пока вы не используете Get-Job | Receive-Job получить результат работы.

редактировать проблема в том, что ваш DisplayMessage функция находится в локальной области сценария, в то время как ваш eventhandler работает в другом родительская область (например, глобальная, которая является областью сеанса), поэтому она не может найти вашу функцию. Если вы создадите функцию в глобальной области и вызовете ее из глобальной области, она будет работать.

Я изменил ваш сценарий, чтобы сделать это сейчас. Я также изменил scriptblock и незарегистрировал события, когда скрипт будет выполнен, поэтому вы не получите 10-кратные сообщения при запуске скрипта несколько раз: -)

безымянный.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

простой способ включить локальные функции в фоновое задание:

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

Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null