短的問題描述:
Powershell的運行空間和色彩輸出
我需要從我的腳本會生成額外的PowerShell運行空間非穿插輸出。我需要能夠設置輸出中各行的顏色。
長問題描述:
我寫了一個腳本來更新部署到多個遠程服務器供應商的應用程序。原本腳本一次只能部署一個更新,但我有一個新的要求,即我現在交互式地處理多個更新。所以我重寫了我的腳本以使用運行空間,以便我可以在所有服務器上打開會話,初始化它們,然後爲每次更新用戶輸入一個運行空間,爲每個初始化會話創建一個運行空間並完成部署工作。我以前使用過工作,但必須進入運行空間,因爲您無法將PSSession傳遞給工作。
我現在有我的腳本工作,但輸出不太理想。作業版本有很好的輸出,因爲我可以調用Receive-Job並獲取所有按照線程分組的輸出。此外,我可以使用寫主機來記錄允許彩色響應的輸出(即在更新失敗時應用紅色文本)。我已經能夠獲得作業的運行空間版本,以便按線程對輸出進行分組,但只能使用寫入輸出。如果我使用寫主機輸出立即發生,導致散佈的輸出這是不可接受的。不幸的是,寫輸出不允許彩色輸出。設置$ host.UI.RawUI.ForegroundColor也不起作用,因爲如果輸出在設置顏色的時刻沒有發生,則效果不會發生。由於我的輸出直到結束纔會發生,所以$主機設置不再起作用。
下面是一個快速的演示腳本來說明我的困境:
#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()
的輸出如下,除非指定的輸出是灰色的:
我的目標是能夠得到的線是說「一些寫輸出:X」設置爲一種顏色,並且「測試: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 -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
我增加了一個例子來說明我在工作中遇到的問題。請看一下。 – 2013-04-09 14:53:52
您是否嘗試過在-session和-asjob參數中使用invoke-command? Start-Job在本地系統上創建後臺作業。我不明白爲什麼你想創建一個本地後臺作業只是爲了在遠程系統上運行腳本。作爲一項工作,只需調用遠程系統上的腳本即可。 – mjolinor 2013-04-10 03:07:32
我開始本地作業,然後在作業中調用invoke-command。根據部署的成功,我可以使用invoke命令將日誌文件的內容回顯到本地作業。然後,本地作業可以創建本地日誌文件,防止用戶必須登錄到多個服務器才能檢索故障日誌。我會考慮使用invoke-command來獲得相同的結果。 – 2013-04-10 12:59:01