Как вызвать функцию 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