Reputation: 157
I trying to write a script, which show iis pools state with a different color. And I can't understand why script coloring in one color all strings when I use echo. Here script:
$pools = invoke-command -session $session -scriptblock {Import-Module WebAdministration; Get-ChildItem IIS:\AppPools | Where {$_.Name -like "*abc*"}}
$poolsShow = $pools | Select-Object -Property name, state
$poolsShow | ForEach-Object {
if($_.state -eq "Started") {
$Host.UI.RawUI.ForegroundColor = "Green";
echo $_;
[Console]::ResetColor();
}
if($_.state -eq "Stopped") {
$Host.UI.RawUI.ForegroundColor = "Red";
echo $_;
[Console]::ResetColor();
}
}
It is work if I go through the $pools, but if I select name and state via Select-Object - all strings are coloring in the color of the last service. I have tried via Write-Host - and it's worked, but I didn't find a way, how to format output in one table with a headers only at first line and with the same width in every string.
Upvotes: 3
Views: 2085
Reputation: 439597
To complement Santiago's helpful answer:
As for what you tried:
echo
in PowerShell is a built-in alias for Write-Output
, which does not print directly to the console - instead, it prints to the success output stream.
If the success output stream isn't captured or redirected in a given command, it is eventually printed to the console, after undergoing for-display formatting by PowerShell's formatting system.
Because your output objects have 4 or fewer properties, PowerShell applies tabular formatting by default; that is, the Format-Table
cmdlet is implicitly used, which has a perhaps surprising implication:
[pscustomobject] @{ foo = 1 }; Write-Host 'Why am I printing first??
- see this answer for background information.Therefore, the formatted table's rows only started printing after that delay, so your attempt to control their color one by one with ForEach-Object
was ineffective.
.OutputRendering
property of the $PSStyle
preference variable.Santiago's answer bypasses this problem by using a calculated property to color individual property values rather than trying to control the coloring of the already-formatted representation of the object.
If you want a prepackaged, general-purpose solution, you can use the Out-HostColored
function from this Gist (authored by me), which in your case would make the solution as simple as piping your objects to Out-HostColored.ps1 @{ Started = 'Green'; Stopped = 'Red' }
:
# Download and define function `Out-HostColored` on demand (will prompt).
# To be safe, inspect the source code at the specified URL first.
if (-not (Get-Command -ErrorAction Ignore Out-HostColored1)) {
$gistUrl = 'https://gist.github.com/mklement0/243ea8297e7db0e1c03a67ce4b1e765d/raw/Out-HostColored.ps1'
if ((Read-Host "`n====`n OK to download and define function ``Out-HostColored```n from Gist ${gistUrl}?`n=====`n(y/n)?").Trim() -notin 'y', 'yes') { Write-Warning 'Aborted.'; exit 2 }
Invoke-RestMethod $gistUrl | Invoke-Expression 3>$null
if (-not ${function:Out-HostColored}) { exit 2 }
}
# Emit sample objects and color parts of their formatted representation
# based on regex patterns.
0..5 | ForEach-Object {
[pscustomobject]@{
Name = "Test $_"
State = ('Started', 'Stopped')[$_ % 2]
}
} |
Out-HostColored.ps1 @{ Started = 'Green'; Stopped = 'Red' }
Output:
Add -WholeLine
if you want to color matching lines in full.
The hashtable maps search text patterns to colors.
Whenever a pattern is found in the formatted representations of the input objects, it is colored using the specified color.
Note that the hashtable keys are regexes by default, unless you also specify
-SimpleMatch
.
Started =
with '\bStarted\b' =
in order to only match full words.Upvotes: 2
Reputation: 60683
You can take a similar approach as the one proposed in this answer, the only difference would be that the ANSI Escape Sequences are prepended to the property values of the objects created by Select-Object
. Helpful answer provided by @mklement0 in the same question provides more details on this.
function status {
$ansi = switch($args[0]) {
Stopped { "$([char] 27)[91m" }
Started { "$([char] 27)[32m" }
}
$ansi + $args[0]
}
Invoke-Command -Session $session -ScriptBlock {
Import-Module WebAdministration
Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -like "*abc*" }
} | Select-Object Name, @{ N='Status'; E={ status $_.State }}
A demo using custom objects:
0..5 | ForEach-Object {
[pscustomobject]@{
Name = "Test $_"
Status = ('Started', 'Stopped')[$_ % 2]
}
} | Select-Object Name, @{ N='Status'; E={ status $_.Status }}
Upvotes: 3