[英]Powershell runspaces and color output
簡短的問題描述:
我需要來自我的腳本生成的其他 powershell 運行空間的非散布輸出。 我需要能夠設置輸出中各行的顏色。
長問題描述:
我編寫了一個腳本來將更新部署到多個遠程服務器上的供應商應用程序。 最初,該腳本旨在一次僅部署一個更新,但我現在有一個新要求,即我現在以交互方式處理多個更新。 所以我重新編寫了我的腳本來使用運行空間,這樣我就可以在所有服務器上打開會話,初始化它們,然后對於用戶輸入的每次更新,為每個初始化的會話創建一個運行空間並完成部署工作。 我以前使用過作業,但不得不轉到運行空間,因為您無法將 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”設置為一種顏色,並將“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
我不太理解您不能將PSSession傳遞給工作的說法。 您可以同時使用-Session和-AsJob參數運行Invoke-Command,以針對該會話的作業創建。
至於着色難題,您是否考慮過將Verbose或Debug流用於要成為其他顏色的輸出? Powershell會根據其來源自動將其設置為其他顏色。
這在 Powershell 7 中變得更容易了,當最初提出這個問題時它還不可用。
在 powershell 7 中,您可以使用foreach-object -parallel {} -asjob
命令,然后在所有作業完成后獲取結果。
在這個例子中:
有 5 個不同的工作,有 5 個子步驟
每個步驟都有 0.5 秒的延遲來演示交錯
每個工作都有一個特定於工作的顏色
每個步驟都會獲得特定於該步驟的顏色
輸出采用顏色編碼,並按作業分組
$script = { foreach ( $Step in 1..5 ) { write-host "Job $_" -ForegroundColor @{'1'='red';'2'='yellow';'3'='cyan';'4'='magenta';'5'='green'}[[string]$_] -NoNewLine write-host " Step $Step" -ForegroundColor @{'1'='red';'2'='yellow';'3'='cyan';'4'='magenta';'5'='green'}[[string]$Step] sleep 0.5 } # next step } # end script $Job = 1..5 | ForEach-Object -Parallel $Script -ThrottleLimit 5 -AsJob $job | Wait-Job | Receive-Job
需要注意的是:
$job | Wait-Job | Receive-Job
的挑戰$job | Wait-Job | Receive-Job
$job | Wait-Job | Receive-Job
$job | Wait-Job | Receive-Job
是如果任何一個作業掛起,那么這個命令將永遠不會返回。 有大量的帖子描述了如何處理這個限制。
Foreach-object
和Foreach-Object -Parallel
是兩種完全不同的動物。 前者可以調用預定義的函數,而后者只能訪問腳本塊中定義的內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.