2013-03-20 11 views
6

私はthis questionthis questionを見ましたが、私の問題を解決できませんでした。Start-Jobからスクリプト内でpowershell関数を呼び出す方法は?

ここに状況があります。 スクリプト(.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つのバックグラウンドジョブを作成し、バックグラウンドジョブからホストにメッセージを渡すためにイベントを使用しています。私は、PowerShellホストが "Processing Starts"と "Processing Ends"を1回、 "Starting work"と "Ending work"をそれぞれ3回表示すると期待します。しかし、代わりに私はコンソールに "Starting work"/"Ending work"と表示されていません。

イベントもPowerShellでジョブとして扱われるので、私は、仕事を得る行うとき、私はイベントジョブに関連付けられている次のようなエラーを見ることができます:

{用語「DisplayMessageとは」として認識されませんコマンドレットの名前、 関数、スクリプトファイル、または実行可能なプログラム。 名前のスペルを確認するか、パスが含まれていた場合、再びパスが正しいことを確認し、 してみてください}

私の質問を:。我々は(または参照)関数内(DisplayMessageと再利用することができますどのようにケース)は、私がStart-Jobを呼び出すのと同じスクリプトで定義されていますか?出来ますか? 私は、関数/モジュールをStart-Jobに渡すために-InitializationScriptを使うことができますが、私はDisplayMessage関数をスクリプトに2回、InitializationScriptにもう1回書くことは望ましくありません。

答えて

5

バックグラウンドジョブは別々のプロセスで実行されるため、Start-Jobで作成したジョブは、$scriptblockに含める場合を除いて、ファンクションと対話できません。

$scripblockに機能を組み込んでも、Get-Job | Receive-Jobを使用してジョブの結果を受け取るまで、Write-Hostはコンソールに内容を出力しません。あなたのEventHandlerが(セッションスコープであるグローバルのような)異なる親スコープで実行しながら、

EDIT問題は、あなたのDisplayMessage機能はローカルスクリプトスコープであるということですので、あなたの関数を見つけることができません。関数をグローバルスコープで作成し、グローバルスコープから呼び出すと、そのスコープが機能します。

これを行うためにスクリプトを修正しました。私はまた、テスト

Untitled1.ps1

:-)

スクリプトを実行するときに10倍のメッセージを取得することはできませんので

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 
+0

感謝。私はStart-Jobが外部機能と対話できないことを知っています。また、私はWrite-Hostがコンソールに直接出力できないことを知っています。なぜこれを行うためにイベントを使用しているのですか。私の場合、私はホストに書き込むことができますが、イベントで直接「Write-Host」を使用する場合にのみ、関数から呼び出すときは使用できません。 – digitguy

+0

Okey、あなたの質問が間違っていると思います。 :)私の質問の最後に新しい内容を見てください。それはあなたの現在の問題ですか? –

+0

はい、あなたは正しいです。前者は動作しますが後者は動作しません。 – digitguy

6

バックグラウンドジョブでローカル関数を含めるための簡単な方法:

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

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

私はあなたのバックグラウンドジョブでローカル機能を含めるのが好きです!しかし、それは私の問題を解決しませんでした(理由は分かりません:()。)私は上記の@Graimerソリューションを使って私の問題を解決することができました。 – digitguy

+0

私は '{}'ブラケットを使用して、 $ init = {function DoWork {$ function:DoWork}} 'これは、' Start-job'と ' - @ 'を使って同じ方法で呼び出すことができます。 – Richard

+0

私はmjolinorの解決策を得ましたが、私のケースでは、 'init'引数を使って' DoWork'を含めても機能しませんでした。 'DoWork'などの関数/変数を再帰的にインクルードする必要がありました。 – Rob

関連する問題