web-dev-qa-db-ja.com

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つの要素を持つ$ 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回ずつ)書きたくありません。

11
digitguy

バックグラウンドジョブは別のプロセスで実行されるため、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
6
Frode F.

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

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

Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null
8
mjolinor

次のように動作するようにしました。

function CreateTable {
    $table = "" | select ServerName, ExitCode, ProcessID, StartMode, State, Status, Comment
    $table
}

$init=[scriptblock]::create(@"
  function CreateTable {$function:createtable}
"@)
0
KitKatLut