この質問 と この質問 を見ましたが、私の問題の解決策が見つかりませんでした。
というわけで、状況は次のとおりです。スクリプト(.ps1)ファイルにDoWorkとDisplayMessageの2つの関数があります。コードは次のとおりです。
### 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ホストが「Processing Starts」と「Processing Ends」をそれぞれ1回、「Starting work」と「Ending work」をそれぞれ3回表示すると予想しています。しかし、代わりにコンソールに「作業の開始」/「作業の終了」が表示されません。
また、イベントはPowerShellでジョブとして扱われるため、Get-Jobを実行すると、イベントジョブに関連する次のエラーが表示されます。
{「DisplayMessage」という用語は、コマンドレット、関数、スクリプトファイル、または操作可能なプログラムの名前として認識されません。名前のつづりを確認するか、パスが含まれている場合は、パスが正しいことを確認して再試行してください。}
私の質問:Start-Jobを呼び出しているのと同じスクリプトで定義されている関数(この場合はDisplayMessage)を再利用(または参照)するにはどうすればよいですか?出来ますか? -InitializationScriptを使用して関数/モジュールをStart-Jobに渡すことができますが、DisplayMessage関数を2回(スクリプトとInitializationScriptで1回ずつ)書きたくありません。
バックグラウンドジョブは別のプロセスで実行されるため、Start-Job
で作成したジョブは、$scriptblock
に含めない限り、関数と対話できません。
関数を$scripblock
に含めた場合でも、Write-Host
を使用してジョブの結果を受け取るまで、Get-Job | Receive-Job
はその内容をコンソールに出力しません。
[〜#〜] edit [〜#〜]問題は、DisplayMessage
関数がローカルスクリプトスコープにあり、eventhandlerが異なる親スコープ(globalなどはセッションスコープです)、関数を見つけることができません。グローバルスコープで関数を作成し、グローバルスコープから呼び出すと、機能します。
これを行うためにスクリプトを修正しました。また、スクリプトの実行時に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
バックグラウンドジョブにローカル関数を含める簡単な方法:
$init=[scriptblock]::create(@"
function DoWork {$function:DoWork}
"@)
Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null
次のように動作するようにしました。
function CreateTable {
$table = "" | select ServerName, ExitCode, ProcessID, StartMode, State, Status, Comment
$table
}
$init=[scriptblock]::create(@"
function CreateTable {$function:createtable}
"@)