2

問題の簡単な説明:

スクリプトが生成する追加の PowerShell 実行空間からの散在していない出力が必要です。出力の個々の行の色を設定できる必要があります。

問題の長い説明:

複数のリモート サーバー上のベンダー アプリケーションに更新を展開するスクリプトを作成しました。当初、スクリプトは一度に 1 つの更新のみを展開することを目的としていましたが、複数の更新を対話的に処理するという新しい要件があります。そのため、実行空間を使用するようにスクリプトを書き直して、すべてのサーバーでセッションを開いて初期化できるようにしました。その後、ユーザーが更新を入力するたびに、初期化されたセッションごとに実行空間が作成され、展開作業が完了します。以前はジョブを使用していましたが、PSSession をジョブに渡すことができないため、実行空間に移動する必要がありました。

スクリプトが機能するようになりましたが、出力は望ましくありません。Receive-Job を呼び出して、スレッドごとにグループ化されたすべての出力を取得できるため、ジョブ バージョンの出力は適切でした。また、write-host を使用して出力をログに記録することもできます。これにより、色付きの応答が可能になります (つまり、更新の適用に失敗した場合は赤いテキスト)。ジョブの実行空間バージョンを取得して、出力をスレッドごとにグループ化することができましたが、書き込み出力を使用する必要がありました。write-host を使用するとすぐに出力が発生し、許容できない散在する出力が発生します。残念ながら、書き込み出力はカラー出力を許可していません。$host.UI.RawUI.ForegroundColor の設定も機能しません。これは、色が設定された時点で出力が発生しない場合、効果が発生しないためです。出力は最後まで行われないため、$host 設定は無効になります。

以下は、私の悩みを説明するための簡単なデモ スクリプトです。

#Runspacing output example. Fix interleaving
cls

#region Setup throttle, pool, iterations
$iterations = 5
$throttleLimit = 2
write-host ('Throttled to ' + $throttleLimit + ' concurrent threads')

$iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$Pool = [runspacefactory]::CreateRunspacePool(1,$throttleLimit,$iss,$Host)
$Pool.Open()    
#endregion

#This works because the console color is set at the time output occurs
$OrigfC = $host.UI.RawUI.ForegroundColor
$host.UI.RawUI.ForegroundColor = 'red'
write-host 'THIS IS RED TEXT!'
start-sleep 2
$host.UI.RawUI.ForegroundColor = $OrigfC    

#define code to run off the main thread
$scriptBlock = { 
    $nl = ([Environment]::NewLine.Chars(0)+[Environment]::NewLine.Chars(1))               

    #This does not work because output won't occur until after color has been reset
    $OrigfC = $host.UI.RawUI.ForegroundColor
    $host.UI.RawUI.ForegroundColor = 'yellow'
    write-output ($nl + '  TEST: ' + $args[0])
    Write-Output ('  Some write-output: ' + $args[0])
    Start-Sleep 1
    write-host ('  Some write-host: ' + $args[0]) -ForegroundColor Cyan # notice write-host occurs immediately
    Start-Sleep 1
    $host.UI.RawUI.ForegroundColor = $OrigfC
}

#Start new runspaces
$threads = @()  
$handles = @(for($x = 1; $x -le $iterations; $x++)
{
    $powerShell = [PowerShell]::Create().AddScript($scriptBlock).AddParameters(@($x))
    $powershell.RunspacePool = $Pool
    $powerShell.BeginInvoke()
    $threads += $powerShell
})    

#Wait for threads to complete
$completedCount = 0
$completed = ($handles | where-object {$_.IsCompleted -eq $true}).count
while($handles.IsCompleted.Contains($false))
{
    if($completedCount -ne ($handles | where-object {$_.IsCompleted -eq $true}).count)
    {
        $completedCount = ($handles | where-object {$_.IsCompleted -eq $true}).count
        write-host ('Threads Completed: ' + $completedCount + ' of ' + $iterations) 
    }
    write-host '.' -nonewline
    Start-Sleep 1 
}
write-host ('Threads Completed: ' + ($handles | where-object {$_.IsCompleted -eq $true}).count + ' of ' + $iterations) 

#output from threads
for($z = 0; $z -lt $handles.Count; $z++)
{
    $threads[$z].EndInvoke($handles[$z]) #causes output
    $threads[$z].Dispose()
    $handles[$z] = $null 
}

$Pool.Dispose()   

出力が灰色で指定されていない限り、出力は以下のとおりです。
私の目標は、「Some write-output: X」という行を 1 つの色に設定し、「TEST: X」を別の色に設定することです。 . アイデア?私はpowershellプロンプトから実行していることに注意してください。ISE で実行すると、異なる結果が得られます。

Throttled to 2 concurrent threads
THIS IS RED TEXT!                #Outputs in Red
.  Some write-host: 1            #Outputs in Cyan
  Some write-host: 2             #Outputs in Cyan
.Threads Completed: 2 of 5       #Outputs in Yellow
.  Some write-host: 3            #Outputs in Cyan
  Some write-host: 4             #Outputs in Cyan
.Threads Completed: 4 of 5       #Outputs in Yellow
.  Some write-host: 5            #Outputs in Cyan
.Threads Completed: 5 of 5

  TEST: 1
  Some write-output: 1

  TEST: 2
  Some write-output: 2

  TEST: 3
  Some write-output: 3

  TEST: 4
  Some write-output: 4

  TEST: 5
  Some write-output: 5

編集: Mjolinor's Answer に対処する別の例を追加します。M さん、その通りです。セッションをジョブに渡すことができます。上記の例を単純化しすぎました。関数をジョブに送信する以下の例を検討してください。この行 ( if(1 -ne 1){MyFunc -ses $args[0]} ) を以下でコメントアウトすると、実行されます。行がコメント化されていない場合、セッション (System.Management.Automation.Runspaces.PSSession 型) は Deserialized.System.Management.Automation.Runspaces.PSSession 型に変換されますが、MyFunc 呼び出しにヒットできません。私はこれを理解することができなかったので、実行空間に向かって動き始めました。ジョブ指向のソリューションがあると思いますか?

cls
$ses = New-PSSession -ComputerName XXXX
Write-Host ('Outside job: '+$ses.GetType())

$func = {
    function MyFunc {
        param([parameter(Mandatory=$true)][PSSession]$ses)
        Write-Host ('Inside fcn: '+$ses.GetType())
    }
}    

$scriptBlock = {
    Write-Host ('Inside job: '+$args[0].GetType())
    if(1 -ne 1){MyFunc -ses $args[0]}
    }

Start-Job -InitializationScript $func -ScriptBlock $scriptBlock -Args @($ses) | Out-Null                 
While (Get-Job -State "Running") { }    
Get-Job | Receive-Job          
Remove-Job *     
Remove-PSSession -Session $ses
4

2 に答える 2

2

PSSession をジョブに渡すことができないというステートメントがよくわかりません。-Session パラメーターと -AsJob パラメーターの両方を指定して Invoke-Command を実行し、セッションを対象とした at ジョブを作成できます。

色の難問に関して言えば、別の色にしたい出力に Verbose または Debug ストリームを使用することを検討しましたか? Powershell は、元のストリームに応じて自動的に別の色にする必要があります。

于 2013-04-08T19:46:27.467 に答える