Skip to content

Instantly share code, notes, and snippets.

@blakedrumm
Last active October 16, 2024 01:47
Show Gist options
  • Save blakedrumm/0842e0c98cdbdf2f5bc1657f38422695 to your computer and use it in GitHub Desktop.
Save blakedrumm/0842e0c98cdbdf2f5bc1657f38422695 to your computer and use it in GitHub Desktop.
Enhance your PowerShell scripts with Write-Console for displaying messages with optional timestamps and customizable colors. Utilize dynamic rainbow mode to create colorful, segmented outputs where each character has a unique color, ensuring no consecutive duplicates. Improve readability and organization in your console applications effortlessly…
<#
.SYNOPSIS
Provides advanced console output functionality with customizable timestamps and colorization options.
.DESCRIPTION
This script contains the `Write-Console` function to enhance PowerShell console output.
It offers features such as customizable timestamps, colored text, and segmented messages with advanced formatting.
A "Rainbow" mode is included, which applies unique colors to each character, improving readability and visual organization.
.EXAMPLE
Write-Console -Text "Process started successfully." -ForegroundColor Green -BackgroundColor Black -TimestampColor White -SeparatorColor Cyan
Display a simple colored message with a timestamp
.EXAMPLE
$messageSegments = @(
@{ Text = "Error: "; ForegroundColor = 'Red' },
@{ Text = "Invalid input detected."; ForegroundColor = 'White'; BackgroundColor = 'DarkRed' }
)
Write-Console -MessageSegments $messageSegments -SeparatorColor Magenta -NoTimestamp
Display a complex, colorized message without a timestamp
.EXAMPLE
Write-Console -Text "This is pretty cool" -ForegroundColor Rainbow -TimestampColor Rainbow -SeparatorColor Rainbow -NoNewLine
Display a rainbow-colored message without a newline at the end
.NOTES
Author: Blake Drumm
Date Created: 2024-10-05
Date Modified: 2024-10-14
- Ensure the console supports the specified colors.
- The `-NoTimestamp` switch omits the timestamp from the output.
- The `-NoNewLine` switch omits the newline after the message.
- When using arrays for parameters like `-MessageSegments`, ensure parameter names are explicitly provided to avoid positional binding issues.
- The "Rainbow" feature cycles through a predefined set of colors for each character in the output.
- This script adheres to PowerShell best practices, including structured documentation, parameter validation, and examples for ease of use.
.LINK
https://gist.github.com/blakedrumm/0842e0c98cdbdf2f5bc1657f38422695
.LINK
https://blakedrumm.com/
#>
function Time-Stamp
{
$TodaysDate = [DateTime]::Now
return "$($TodaysDate.ToShortDateString()) $($TodaysDate.ToLongTimeString())"
}
function Write-Console
{
[CmdletBinding()]
param (
# =====================================================
# Parameters for the Write-Console Function
# =====================================================
# Optional parameter for a simple text message
[Parameter(Mandatory = $false)]
[string]$Text,
# Optional parameter for an array of message segments (for colorized output)
[Parameter(Mandatory = $false)]
[Array]$MessageSegments,
# Optional foreground color for the timestamp text
[Parameter()]
[ValidateSet(
'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta',
'DarkYellow', 'Gray', 'DarkGray', 'Cyan', 'Green', 'Red',
'Magenta', 'Yellow', 'White', 'Default', 'Rainbow')]
[string]$TimestampColor,
# Optional background color for the timestamp
[Parameter()]
[ValidateSet(
'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta',
'DarkYellow', 'Gray', 'DarkGray', 'Cyan', 'Green', 'Red',
'Magenta', 'Yellow', 'White')]
[string]$TimestampBackgroundColor,
# Optional foreground color for the text (used with -Text parameter)
[Parameter()]
[ValidateSet(
'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta',
'DarkYellow', 'Gray', 'DarkGray', 'Cyan', 'Green', 'Red',
'Magenta', 'Yellow', 'White', 'Default', 'Rainbow')]
[string]$ForegroundColor,
# Optional background color for the text (used with -Text parameter)
[Parameter()]
[ValidateSet(
'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta',
'DarkYellow', 'Gray', 'DarkGray', 'Cyan', 'Green', 'Red',
'Magenta', 'Yellow', 'White')]
[string]$BackgroundColor,
# Optional foreground color for the separator text
[Parameter()]
[ValidateSet(
'Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta',
'DarkYellow', 'Gray', 'DarkGray', 'Cyan', 'Green', 'Red',
'Magenta', 'Yellow', 'White', 'Default', 'Rainbow')]
[string]$SeparatorColor,
# Switch to suppress the timestamp output
[Parameter(Mandatory = $false)]
[switch]$NoTimestamp,
# Switch to suppress the newline after the message
[Parameter(Mandatory = $false)]
[switch]$NoNewLine
)
# GitHub Gist link: https://gist.github.com/blakedrumm/0842e0c98cdbdf2f5bc1657f38422695
# =====================================================
# Initialize Variables
# =====================================================
# Initialize the separator as an empty string
$separator = ""
# Set the log file path (adjust this path as needed)
$logFilePath = "C:\Temp\script.log"
# Ensure the log directory exists
$logDirectory = [System.IO.Path]::GetDirectoryName($logFilePath)
if (!(Test-Path -Path $logDirectory))
{
# Create the directory if it doesn't exist
New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null
}
# Initialize the script-scoped log buffer if not already initialized
if (-not ($script:logBuffer))
{
$script:logBuffer = ""
}
# =====================================================
# Helper Function to Generate Unique Color Index
# =====================================================
function Get-UniqueColorIndex
{
param (
[int]$Min = 0,
[int]$Max = 7
)
# Initialize a script-scoped variable to store the previous color index
if (-not ($script:previousColorIndex -ne $null))
{
$script:previousColorIndex = -1
}
do
{
# Generate a new random color index
$newColorIndex = Get-Random -Minimum $Min -Maximum $Max
}
while ($newColorIndex -eq $script:previousColorIndex)
# Update the previous color index
$script:previousColorIndex = $newColorIndex
return $newColorIndex
}
# =====================================================
# Timestamp Handling Section
# =====================================================
# Check if the NoTimestamp switch is NOT set
if (-not $NoTimestamp)
{
# Generate the timestamp using the Time-Stamp function and trim any whitespace
$timestamp = (Time-Stamp).Trim()
# Append the timestamp to the log buffer
$script:logBuffer += $timestamp
# Handle Rainbow for TimestampColor
if ($TimestampColor -eq 'Rainbow')
{
# Define the rainbow color sequence
$rainbowColors = @('Red', 'DarkYellow', 'Yellow', 'Green', 'Cyan', 'DarkMagenta', 'Magenta')
# Iterate through each character and apply a color
foreach ($char in $timestamp.ToCharArray())
{
# Get a unique color index
$colorIndex = Get-UniqueColorIndex -Min 0 -Max 7
$currentColor = $rainbowColors[$colorIndex % $rainbowColors.Count]
# Prepare parameters for Write-Host for each character
$charParams = @{
'Object' = $char
'NoNewline' = $true
'ForegroundColor' = $currentColor
}
# Include BackgroundColor only if it's specified
if ($PSBoundParameters.ContainsKey('TimestampBackgroundColor') -and $TimestampBackgroundColor)
{
$charParams['BackgroundColor'] = $TimestampBackgroundColor
}
# Output the character with the specified color
Write-Host @charParams
}
}
else
{
# Prepare parameters for Write-Host to output the timestamp
$timeStampParams = @{
'Object' = $timestamp # The timestamp text
'NoNewline' = $true # Keep the cursor on the same line
}
# If a foreground color for the timestamp is provided, include it
if ($PSBoundParameters.ContainsKey('TimestampColor') -and $TimestampColor -and $TimestampColor -ne 'Default')
{
$timeStampParams['ForegroundColor'] = $TimestampColor
}
# If a background color for the timestamp is provided, include it
if ($PSBoundParameters.ContainsKey('TimestampBackgroundColor') -and $TimestampBackgroundColor)
{
$timeStampParams['BackgroundColor'] = $TimestampBackgroundColor
}
# Output the timestamp with the specified colors (if any)
Write-Host @timeStampParams
}
# =====================================================
# Separator Handling Section
# =====================================================
# Prepare the separator text
$separator = " - "
# Append the separator to the log buffer
$script:logBuffer += $separator
# Output the separator
$separatorParams = @{
'Object' = $separator # Separator text
'NoNewline' = $true # Keep the cursor on the same line
}
# If a foreground color for the separator is provided, include it
if ($PSBoundParameters.ContainsKey('SeparatorColor') -and $SeparatorColor -and $SeparatorColor -ne 'Default')
{
$separatorParams['ForegroundColor'] = $SeparatorColor
}
# Output the separator with the specified color (if any)
Write-Host @separatorParams
}
# =====================================================
# Message Output Handling Section
# =====================================================
# Determine whether to handle the -Text or -MessageSegments parameter
if ($PSBoundParameters.ContainsKey('Text') -and $null -ne $Text)
{
# Append the text to the log buffer
$script:logBuffer += $Text
if ($ForegroundColor -eq 'Rainbow')
{
# Define the rainbow color sequence
$rainbowColors = @('Red', 'DarkYellow', 'Yellow', 'Green', 'Cyan', 'DarkMagenta', 'Magenta')
# Iterate through each character and apply a color
foreach ($char in $Text.ToCharArray())
{
# Get a unique color index
$colorIndex = Get-UniqueColorIndex -Min 0 -Max 7
$currentColor = $rainbowColors[$colorIndex % $rainbowColors.Count]
# Prepare parameters for Write-Host for each character
$charParams = @{
'Object' = $char
'NoNewline' = $true
'ForegroundColor' = $currentColor
}
# Include BackgroundColor only if it's specified
if ($PSBoundParameters.ContainsKey('BackgroundColor') -and $BackgroundColor -and $BackgroundColor -ne 'Default')
{
$charParams['BackgroundColor'] = $BackgroundColor
}
# Output the character with the specified color
Write-Host @charParams
}
}
else
{
# Prepare a hashtable to hold parameters for Write-Host
$writeHostParams = @{
'Object' = $Text # The text message to display
'NoNewline' = $true # Keep the cursor on the same line
}
# If a foreground color is specified for the text, include it
if ($PSBoundParameters.ContainsKey('ForegroundColor') -and $ForegroundColor -and $ForegroundColor -ne 'Default')
{
$writeHostParams['ForegroundColor'] = $ForegroundColor
}
# If a background color is specified for the text, include it
if ($PSBoundParameters.ContainsKey('BackgroundColor') -and $BackgroundColor -and $BackgroundColor -ne 'Default')
{
$writeHostParams['BackgroundColor'] = $BackgroundColor
}
# Output the text using Write-Host with the specified colors (if any)
Write-Host @writeHostParams
}
}
elseif ($PSBoundParameters.ContainsKey('MessageSegments') -and $null -ne $MessageSegments)
{
foreach ($segment in $MessageSegments)
{
# Append the segment text to the log buffer
$script:logBuffer += $segment.Text
if ($segment.ForegroundColor -eq 'Rainbow')
{
# Define the rainbow color sequence
$rainbowColors = @('Red', 'DarkYellow', 'Yellow', 'Green', 'Cyan', 'DarkMagenta', 'Magenta')
# Iterate through each character and apply a color
foreach ($char in $segment.Text.ToCharArray())
{
# Get a unique color index
$colorIndex = Get-UniqueColorIndex -Min 0 -Max 7
$currentColor = $rainbowColors[$colorIndex % $rainbowColors.Count]
# Prepare parameters for Write-Host for each character
$charParams = @{
'Object' = $char
'NoNewline' = $true
'ForegroundColor' = $currentColor
}
# Include BackgroundColor only if it's specified
if ($segment.BackgroundColor -and $segment.BackgroundColor -ne 'Default')
{
$charParams['BackgroundColor'] = $segment.BackgroundColor
}
# Output the character with the specified color
Write-Host @charParams
}
}
else
{
# Prepare a hashtable to hold parameters for Write-Host for each segment
$writeHostParams = @{
'Object' = $segment.Text # The text for this segment
'NoNewline' = $true # Keep the cursor on the same line
}
# If a foreground color is specified for this segment, include it
if ($segment.ForegroundColor -and $segment.ForegroundColor -ne 'Default')
{
$writeHostParams['ForegroundColor'] = $segment.ForegroundColor
}
# If a background color is specified for this segment, include it
if ($segment.BackgroundColor -and $segment.BackgroundColor -ne 'Default')
{
$writeHostParams['BackgroundColor'] = $segment.BackgroundColor
}
# Output the segment text using Write-Host with the specified colors (if any)
Write-Host @writeHostParams
}
}
}
else
{
# If neither -Text nor -MessageSegments is provided, output a warning
$warningMessage = "Error: Please provide either -Text or -MessageSegments"
# Append the warning message to the log buffer
$script:logBuffer += $warningMessage
# Output the warning to the console
Write-Warning $warningMessage
}
# =====================================================
# Newline Handling and Logging Section
# =====================================================
# Add a newline after the message unless NoNewLine is specified
if (-not $NoNewLine)
{
# Output a newline to the console
Write-Host ""
# Write the accumulated log buffer to the log file
try
{
# Append the log buffer to the log file
Add-Content -Path $logFilePath -Value $script:logBuffer
}
catch
{
# Output a warning if writing to the log file fails
Write-Warning "Failed to write to log file: $_"
}
# Clear the log buffer
$script:logBuffer = ""
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment