Multithreading Powershell运行空间和颜色输出

Multithreading Powershell运行空间和颜色输出,multithreading,powershell,Multithreading,Powershell,短期发行说明: 我需要脚本生成的其他powershell运行空间的非分散输出。我需要能够设置输出中各行的颜色 长问题描述: 我编写了一个脚本,将更新部署到多个远程服务器上的供应商应用程序。最初,该脚本旨在一次只部署一个更新,但我有一个新的要求,即我现在以交互方式处理多个更新。因此,我重新编写了使用运行空间的脚本,这样我就可以在所有服务器上打开会话,初始化它们,然后为每次更新用户输入为每个初始化的会话创建一个运行空间,并完成部署工作。我以前使用过作业,但由于无法将PSSession传递给作业,所以

短期发行说明:

我需要脚本生成的其他powershell运行空间的非分散输出。我需要能够设置输出中各行的颜色

长问题描述:

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

现在我的脚本可以正常工作了,但是输出不太理想。作业版本有很好的输出,因为我可以调用Receive job并按线程对所有输出进行分组。此外,我还可以使用写主机来记录输出,这允许彩色响应(即更新失败时的红色文本)。我已经能够让我的作业运行空间版本按线程对输出进行分组,但只能使用写输出。如果我使用写主机输出立即发生,导致不可接受的分散输出。不幸的是,写入输出不允许彩色输出。设置$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()   
输出如下,除非指定,否则输出为灰色:
我的目标是能够将表示“somewrite-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、 你是对的,我可以通过会议的工作;我把上面的例子过于简化了。请考虑下面的例子,我向作业发送一个函数。如果这一行(如果(1-NE1){MyFunc-ses$args[0]})在下面被注释掉,它将运行。如果未注释掉该行,则会话(类型System.Management.Automation.Runspaces.PSSession)将转换为反序列化的.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

我不太明白那种说法,即你不能把一个职位转让给一份工作。您可以使用-Session和-AsJob参数运行Invoke命令,创建针对会话的at作业


至于着色难题,您是否考虑过将详细或调试流用于您希望成为不同颜色的输出?Powershell应根据它来自的流自动将其设置为不同的颜色

我添加了一个额外的例子来说明我在工作中遇到的问题。请看一看。您是否尝试过使用带有-session和-asjob参数的invoke命令?开始作业在本地系统上创建后台作业。我不明白为什么要创建本地后台作业,只是为了在远程系统上运行脚本。只需将远程系统上的脚本作为作业调用即可。我启动本地作业,然后在作业内调用invoke命令。基于部署的成功,我可以使用invoke命令将日志文件的内容回显到本地作业。然后,本地作业可以创建本地日志文件,从而避免用户必须登录多个服务器才能检索故障日志。我将研究如何使用invoke-command实现相同的结果。感谢所有的帮助。使用invoke命令作为作业方法,我能够使脚本完全按照所需工作。