11

この質問この質問を見ましたが、問題の解決策が見つかりませんでした。

状況は次のとおりです。スクリプト (.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ホストが「処理開始」と「処理終了」を1回、「作業開始」と「作業終了」をそれぞれ3回表示することを期待します。しかし、代わりに、コンソールに「作業の開始」/「作業の終了」が表示されません。

イベントも powershell のジョブとして扱われるため、Get-Job を実行すると、イベント ジョブに関連する次のエラーが表示されます。

{'DisplayMessage' という用語は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。名前のスペルを確認するか、パスが含まれている場合は、パスが正しいことを確認してから、もう一度やり直してください。}

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

4

3 に答える 3

8

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

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

Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null
于 2013-03-20T10:27:19.843 に答える
6

バックグラウンドジョブは別のプロセスで実行されるため、作成するジョブは、Start-Jobに含めない限り関数と対話できません$scriptblock

に関数を含めたとしても$scripblock、ジョブの結果を受け取るためにWrite-Host使用するまで、そのコンテンツをコンソールに出力しませんでした。Get-Job | Receive-Job

編集問題はDisplayMessage、イベントハンドラーが別の親スコープ(セッションスコープであるグローバルなど)で実行されているときに関数がローカルスクリプトスコープにあるため、関数が見つからないことです。グローバルスコープで関数を作成し、グローバルスコープから呼び出すと、機能します。

これを行うようにスクリプトを変更しました。また、scriptblockに変更し、スクリプトが完了したときにイベントの登録を解除して、スクリプトを複数回実行したときに10倍のメッセージが表示されないようにしました:-)

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
于 2013-03-20T10:15:42.827 に答える