Skip to content

Add Support for 4bit ANSI Color codes #16

@0-0-1-0-1-0-1-0

Description

@0-0-1-0-1-0-1-0

I was hoping to maybe add a feature that can come in handy with regards to using Write-Color within azure pipelines.

The TTY output console in Azure Pipelines supports 4 bit ANSI color coding of the output. Making this useful for others to use in Azure Pipelines powershell scripts.

Works on PoSH5 and 7

I modified the script to account for this manually and it has worked well for us. Basically i added a swtich for ANSI, probably could add a 4bit and 8bit switch, but 4bit is all I needed.

It then uses the ANSI codes for output. Figured I throw this your way and see if it could be merged with the package and have the nuget/PoSH gallery updated to support as others might find this useful.

function Write-Color {
    <#
    .SYNOPSIS
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.

    .DESCRIPTION
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.

    It provides:
    - Easy manipulation of colors,
    - Logging output to file (log)
    - Nice formatting options out of the box.
    - Ability to use aliases for parameters

    .PARAMETER Text
    Text to display on screen and write to log file if specified.
    Accepts an array of strings.

    .PARAMETER Color
    Color of the text. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White

    .PARAMETER BackGroundColor
    Color of the background. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White

    .PARAMETER StartTab
    Number of tabs to add before text. Default is 0.

    .PARAMETER LinesBefore
    Number of empty lines before text. Default is 0.

    .PARAMETER LinesAfter
    Number of empty lines after text. Default is 0.

    .PARAMETER StartSpaces
    Number of spaces to add before text. Default is 0.

    .PARAMETER LogFile
    Path to log file. If not specified no log file will be created.

    .PARAMETER DateTimeFormat
    Custom date and time format string. Default is yyyy-MM-dd HH:mm:ss

    .PARAMETER LogTime
    If set to $true it will add time to log file. Default is $true.

    .PARAMETER LogRetry
    Number of retries to write to log file, in case it can't write to it for some reason, before skipping. Default is 2.

    .PARAMETER Encoding
    Encoding of the log file. Default is Unicode.

    .PARAMETER ShowTime
    Switch to add time to console output. Default is not set.

    .PARAMETER NoNewLine
    Switch to not add new line at the end of the output. Default is not set.

    .PARAMETER NoConsoleOutput
    Switch to not output to console. Default all output goes to console.

    .EXAMPLE
    Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow

    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan

    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1

    .EXAMPLE
    Write-Color "1. ", "Option 1" -Color Yellow, Green
    Write-Color "2. ", "Option 2" -Color Yellow, Green
    Write-Color "3. ", "Option 3" -Color Yellow, Green
    Write-Color "4. ", "Option 4" -Color Yellow, Green
    Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1

    .EXAMPLE
    Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss"
    Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt"

    .EXAMPLE
    Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow
    Write-Color -t "my text" -c yellow -b green
    Write-Color -text "my text" -c red

    .EXAMPLE
    Write-Color -Text "Testuję czy się ładnie zapisze, czy będą problemy" -Encoding unicode -LogFile 'C:\temp\testinggg.txt' -Color Red -NoConsoleOutput

    .NOTES
    Understanding Custom date and time format strings: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
    Project support: https://github.com/EvotecIT/PSWriteColor
    Original idea: Josh (https://stackoverflow.com/users/81769/josh)

    #>
    [alias('Write-Colour')]
    [CmdletBinding()]
    param (
        [alias ('T')] [String[]]$Text,
        [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White,
        [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null,
        [alias ('Indent')][int] $StartTab = 0,
        [int] $LinesBefore = 0,
        [int] $LinesAfter = 0,
        [int] $StartSpaces = 0,
        [alias ('L')] [string] $LogFile = '',
        [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss',
        [alias ('LogTimeStamp')][bool] $LogTime = $true,
        [int] $LogRetry = 2,
        [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode',
        [switch] $ShowTime,
        [switch] $NoNewLine,
        [alias('HideConsole')][switch] $NoConsoleOutput,
        [switch] $ANSI
    )
    if (-not $NoConsoleOutput) {
        $DefaultColor = $Color[0]
        if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) {
            Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated."
            return
        }
        if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } } # Add empty line before
        if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewline } }  # Add TABS before text
        if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewline } }  # Add SPACES before text
        if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline } # Add Time before output

        if ($ANSI) {
            $ANSIColors = @{
                Black = "$([char]0x1b)[30m"; Blue = "$([char]0x1b)[34m"; Green = "$([char]0x1b)[32m"; Cyan = "$([char]0x1b)[36m";
                Red = "$([char]0x1b)[31m"; Magenta = "$([char]0x1b)[35m"; Yellow = "$([char]0x1b)[33m"; White = "$([char]0x1b)[37m";
                DarkGray = "$([char]0x1b)[90m"; DarkBlue = "$([char]0x1b)[94m"; DarkGreen = "$([char]0x1b)[92m"; DarkCyan = "$([char]0x1b)[96m";
                DarkRed = "$([char]0x1b)[91m"; DarkMagenta = "$([char]0x1b)[95m"; DarkYellow = "$([char]0x1b)[93m"; Gray = "$([char]0x1b)[97m"
            }
            $ANSIBackgroundColors = @{
                Black = "$([char]0x1b)[40m"; Blue = "$([char]0x1b)[44m"; Green = "$([char]0x1b)[42m"; Cyan = "$([char]0x1b)[46m";
                Red = "$([char]0x1b)[41m"; Magenta = "$([char]0x1b)[45m"; Yellow = "$([char]0x1b)[43m"; White = "$([char]0x1b)[47m";
                DarkGray = "$([char]0x1b)[100m"; DarkBlue = "$([char]0x1b)[104m"; DarkGreen = "$([char]0x1b)[102m"; DarkCyan = "$([char]0x1b)[106m";
                DarkRed = "$([char]0x1b)[101m"; DarkMagenta = "$([char]0x1b)[105m"; DarkYellow = "$([char]0x1b)[103m"; Gray = "$([char]0x1b)[107m"
            }
            $ANSIReset = "$([char]0x1b)[0m"
            for ($i = 0; $i -lt $Text.Length; $i++) {
                $colorCode = $ANSIColors[$Color[$i].ToString()]
                $bgColorCode = if ($BackGroundColor) { $ANSIBackgroundColors[$BackGroundColor[$i].ToString()] } else { "" }
                [string]$newString = $Text[$i].Replace("`n", "`n${colorCode}${bgColorCode}")
                $outputText = "${colorCode}${bgColorCode}${newString}${ANSIReset}"
                Write-Host -Object $outputText -NoNewline
            }
        } else {
            if ($Text.Count -ne 0) {
                if ($Color.Count -ge $Text.Count) {
                    # the real deal coloring
                    if ($null -eq $BackGroundColor) {
                        for ($i = 0; $i -lt $Text.Length; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline
                        }
                    } else {
                        for ($i = 0; $i -lt $Text.Length; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline
                        }
                    }
                } else {
                    if ($null -eq $BackGroundColor) {
                        for ($i = 0; $i -lt $Color.Length ; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline
                        }
                        for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline
                        }
                    } else {
                        for ($i = 0; $i -lt $Color.Length ; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline
                        }
                        for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                            Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline
                        }
                    }
                }
            }
        }
        if ($NoNewLine -eq $true) {
            Write-Host -NoNewline
        } else {
            Write-Host
        }
        if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } }  # Add empty line after
    }
    if ($Text.Count -and $LogFile) {
        # Save to file
        $TextToFile = ""
        for ($i = 0; $i -lt $Text.Length; $i++) {
            $TextToFile += $Text[$i]
        }
        $Saved = $false
        $Retry = 0
        Do {
            $Retry++
            try {
                if ($LogTime) {
                    "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                } else {
                    "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                }
                $Saved = $true
            } catch {
                if ($Saved -eq $false -and $Retry -eq $LogRetry) {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Tried ($Retry/$LogRetry))"
                } else {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Retrying... ($Retry/$LogRetry)"
                }
            }
        } Until ($Saved -eq $true -or $Retry -ge $LogRetry)
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions