Powershell运行空间和颜色输出

时间:2013-04-08 18:14:44

标签: multithreading powershell

简短的问题描述:

我需要来自我的脚本产生的额外PowerShell运行空间的非散布输出。我需要能够在输出中设置各行的颜色。

长问题说明:

我编写了一个脚本来将更新部署到多个远程服务器上的供应商应用程序。最初这个脚本只是为了一次只部署一个更新,但我有一个新要求,我现在以交互方式处理多个更新。所以我重写了我的脚本以使用运行空间,以便我可以在所有服务器上打开会话,初始化它们,然后为每个更新用户输入为每个初始化会话创建一个运行空间,并完成部署工作。我以前使用过工作,但不得不去运行空间,因为你无法将PSSession传递给作业。

我现在让我的脚本工作,但输出不太理想。作业版本的输出很好,因为我可以调用Receive-Job并按线程分组所有输出。此外,我可以使用write-host来记录输出,这允许有色响应(即更新无法应用时的红色文本)。我已经能够通过线程将我的runpace版本的作业分组输出,但只能使用write-output。如果我使用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”设置为一种颜色,将“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的答案。 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

1 个答案:

答案 0 :(得分:1)

我不太明白你无法将PSSession传递给工作的声明。您可以使用-Session和-AsJob参数运行Invoke-Command,创建针对会话的作业。

就着色难题而言,您是否考虑过使用Verbose或Debug流输出您想要的不同颜色? Powershell应根据它来自的流自动使其成为不同的颜色。

相关问题