1

There are already several questions about colorizing the PowerShell output, as e.g.:

Which works fine for me with the latest PowerShell 7 version, yet I would like to make my module downwards compatible with Windows PowerShell 5.1 where the color isn't reset if the concerned colored string is being truncated (e.g. in a table format).

Minimal, Reproducible Example:

$Esc = [Char]0x1b
[PSCustomObject]@{
    Path = 'Person.Address'
    Node = "$Esc[36m@{City='New York';..;PostalCode='10021-3100'}$Esc[39m"
    Test = 'Optional'
    Issue = "$Esc[91m[X] The actual item contains unapproved child items: PostalCode. Expected an item with optional child items listed in @{State=@{..};..;City=@{..}}.$Esc[39m"
}

enter image description here

(Note that the color in of the Path property in the second row and the prompt color changed to red, what doesn't happen if the Issue property fits the screen)

Is there for PowerShell 5.1 a way to make sure that the ANSI color is always reset for a string (even it is truncated)?

9
  • 1
    I wouldn't consider this an answer, more of a hack - but change your prompt function to include your ANSI color string? Commented Aug 8, 2024 at 12:00
  • Can't you make $function:prompt prepend $Esc[39m? Commented Aug 8, 2024 at 12:00
  • 1
    @iRon You don't need a calculated property - just set $function:prompt = {"$([char]0x1B)[39mPS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "} Commented Aug 8, 2024 at 12:15
  • 1
    @MathiasR.Jessen, I have updated the question as it appears that it not only concerns the prompt but anything that follows... (I am afraid that there isn't an easy solution for this). Commented Aug 8, 2024 at 12:35
  • 1
    I tried wrapping each char in the string, instead of the string itself, with the ansi codes. Horrible answer if it worked, but it didn't really work in PS5 (chopped 'P' off 'Person.Address' in next line and shorted Issue to '[X]'), yet PS7 was still fine. Clearly PS7, in this context, is designed to handle ansi codes where PS5 isn't. Your only answer afaik, if even possible, is to fix PS5 itself. If there was some way to override and replace an internal function or method with a fixed version, but that is beyond my skill or knowledge. Commented Aug 8, 2024 at 14:26

2 Answers 2

1

Edit:

Turns out, my original answer is not helpful for the issue the question is about. The following screenshot shows weird behavior in Windows PowerShell 5.1. Some testing of colorizing strings via format.ps1xml in Windows PowerShell 5.1 shows garbled results when values are too long for their column. Even when text wrapping is enabled.

I also have to mention that I defined the view to wrap text in columns. It still messes up the color formatting but it does manage to reset the coloring before reaching the next prompt. So that might be a valuable insight. If you're using a format file, add a Wrap tag in front of the TableColumnItems. A quickfix for OP's example would be to send the output through Format-Table and specify the "-Wrap" switch like so: Using OP's example and sending it to Format-Table with the "-Wrap" switch activated would be a possible workaround for the problem. Note that the first prompt is red from when I just sent the object to the console without wrapping. After I output the object with wrapping activated the colors actually get reset.

Original answer:

If you are ok with not color coding your input inline you could define a custom view with a format.ps1xml file. See here how to create your own view.

For example, for one of my modules I have defined a view like this:

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
    <ViewDefinitions>
        <View>
            <Name>Kra.TimeTrackerTime</Name>
            <ViewSelectedBy>
                <TypeName>TimeTrackerTime</TypeName>
            </ViewSelectedBy>
            <GroupBy>
                <!--<PropertyName>Date</PropertyName>-->
                <ScriptBlock>"$($_.Date.DayOfWeek), $($_.Date.ToString('dd.MM.yyyy'))"</ScriptBlock>
                <Label>Group</Label>
            </GroupBy>
            <TableControl>
                <TableHeaders>
                    <TableColumnHeader>
                        <Label>TaskName</Label>
                        <Width>20</Width>
                        <Alignment>Left</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>BookingCode</Label>
                        <Width>14</Width>
                        <Alignment>Left</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Status</Label>
                        <Width>16</Width>
                        <Alignment>Left</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Date</Label>
                        <Width>12</Width>
                        <Alignment>Left</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>StartTime</Label>
                        <Width>5</Width>
                        <Alignment>Right</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>EndTime</Label>
                        <Width>7</Width>
                        <Alignment>Right</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Duration</Label>
                        <Width>8</Width>
                        <Alignment>Right</Alignment>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Comment</Label>
                        <Width>68</Width>
                        <Alignment>Left</Alignment>
                    </TableColumnHeader>
                </TableHeaders>
                <TableRowEntries>
                    <TableRowEntry>
                        <Wrap />
                        <TableColumnItems>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.TaskName
                                    $Esc = [char]27
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.BookingCode
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"}
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.Status -join ";`n"
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if('Pikett' -in $_.Status) {
                                        $Color = '38;2;255;150;60'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"}
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.Date.ToString('dd.MM.yyyy')
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.StartTime.ToString('HH:mm')
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.EndTime.ToString('HH:mm')
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.Duration.ToString('hh\hmm')
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>
                                    $Value = $_.Comment
                                    $Esc = [char]27
                                    if($_.IsBreakTime) {
                                        $Color = '38;2;80;130;255'
                                        $Format = $true
                                    }
                                    if($_.IsRunning) {
                                        $Color = '38;2;66;224;22'
                                        $Format = $true
                                    }
                                    if($_.IsArchived) {
                                        $Color = '38;2;120;120;120'
                                        $Format = $true
                                    }
                                    if($Format) {
                                        "$Esc[$($Color)m$($Value)$Esc[0m"
                                    }
                                    else {
                                        $Value
                                    }
                                </ScriptBlock>
                            </TableColumnItem>
                        </TableColumnItems>
                    </TableRowEntry>
                </TableRowEntries>
            </TableControl>
        </View>
    </ViewDefinitions>
</Configuration>

Mind the "ViewSelectedBy" tag. It has a TypeName specified. In your code, you will have to add that TypeName to any object you want to display in this view like this:

#create the object and store it in a variable
$Obj = [PsCustomObject]@{
    TaskName    = $_.TaskName
    BookingCode = $Task.BookingCode
    Status      = $Status
    IsBreakTime = $Task.IsBreakTime
    Date        = $ObjDate
    StartTime   = $_.StartTime
    EndTime     = $_.EndTime
    Duration    = $Duration
    Comment     = $_.Comment
    IsRunning   = $null -eq $_.EndTime
    IsArchived  = $_.IsArchived
}

#the object has a list of TypeNames
#insert the TypeName specified in the view
$Obj.PsTypeNames.Insert(0,'TimeTrackerTime')
Write-Output $Obj

As long as you have the view loaded as specified in the linked article (you could put that into your PowerShell profile script), any object with the specified TypeName will automatically be presented with that view in the console.

If your code is in a PowerShell module you can add the format.ps1xml file to the module directory and specify the path in the module manifest under "FormatsToProcess".

Sign up to request clarification or add additional context in comments.

2 Comments

@iRon You are right of course. Should have tested this before I wrote the answer. I have now tested this quickly with Windows PowerShell 5.1. It behaves weirdly. The text in the last column was not printed, when it was too long. Text in inner columns was truncated at weird places leading to misalignments in later columns. I will edit my answer...
Thank you (and all others) for thinking along this issue. Although this is not the actual answer where I was looking for, enabling wrapping in the default TableControl view is the best alternative I got so far.
1

Extend [System.Console]::BufferWidth as follows:

[CmdletBinding()]param()
Write-Verbose "Pre:  $($PSVersionTable.PSVersion.Major) $([System.Console]::BufferWidth)"

$Esc = [Char]0x1b
$Output = [PSCustomObject]@{
    Path = 'Person.Address'
    Node = "$Esc[36m@{City='New York';..;PostalCode='10021-3100'}$Esc[39m"
    Test = 'Optional'
    Issue = "$Esc[91m[X] The actual item contains unapproved child items: PostalCode. Expected an item with optional child items listed in @{State=@{..};..;City=@{..}}.$Esc[39m"
}
if ( $PSVersionTable.PSVersion.Major -lt 6 -and $Host.Name -notmatch 'ISE' ) {
    [System.Console]::BufferWidth = 500
}

Write-Verbose "Post: $($PSVersionTable.PSVersion.Major) $([System.Console]::BufferWidth)"

$Output, $Output

Output:

enter image description here

1 Comment

Thanks for the suggestion/workaround, but this has an undesired side effect where the actual control width stays the same but virtual console width just increases to the given sizes causing a horizonal scroll bar to appear at the bottom of the PowerShell console.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.