Как вызвать функцию powershell в script из Start-Job?
Я видел этот вопрос и этот вопрос, но не смог найти решение моей проблемы.
Итак, вот ситуация:
У меня есть две функции DoWork и DisplayMessage в файле script (.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, я вижу следующую ошибку, связанную с заданием события:
{tермин "DisplayMessage" не распознается как имя командлета, функция, файл script или операционная программа. Проверьте правильность написания имя, или если путь был включен, убедитесь, что путь правильный и повторите попытку.}
Мой вопрос: как мы можем повторно использовать (или ссылаться) функцию (DisplayMessage в моем случае), определенную в том же script, с которого я вызываю Start-Job? Является ли это возможным?
Я знаю, что мы можем использовать -InitializationScript для передачи функций/модулей в Start-Job, но я не хочу писать функцию DisplayMessage дважды, одну в script и другую в InitializationScript.
Ответы
Ответ 1
Фоновые задания запускаются в отдельном процессе, поэтому задания, созданные с помощью Start-Job
, не могут взаимодействовать с функциями, если вы не включили их в $scriptblock
.
Даже если вы включили функцию в $scripblock
, Write-Host
не выводит ее содержимое на консоль, пока вы не будете использовать Get-Job | Receive-Job
для получения результата задания.
РЕДАКТИРОВАТЬ. Проблема заключается в том, что ваша функция DisplayMessage
находится в локальном script -scope, в то время как ваш обработчик событий работает в другой родительской области (например, global, которая является областью сеанса), поэтому не может найти вашу функцию. Если вы создадите эту функцию в глобальной области и вызовите ее из глобальной области, она будет работать.
Я изменил ваш script, чтобы сделать это сейчас. Я также модифицировал скриптовый блок и незарегистрировал события, когда выполняется script, поэтому вы не будете получать сообщения 10x при многократном запуске script: -)
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
Ответ 2
Легкий способ включения локальных функций в фоновое задание:
$init=[scriptblock]::create(@"
function DoWork {$function:DoWork}
"@)
Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null
Ответ 3
Я получил это на работу; function CreateTable {$ table = "" | выберите ServerName, ExitCode, ProcessID, StartMode, State, Status, Comment $ table}
$ init = [scriptblock] :: create (@"function CreateTable {$ function: createtable}" @)