function Write-Out {
<#
.SYNOPSIS
Writes multi-color console output with advanced formatting and optional multi-file logging.
.DESCRIPTION
This function prints formatted and colorized output to the PowerShell console. It supports:
- Multiple foreground and background colors per line
- Pre/post spacing, line breaks, and tabulation
- Optional timestamps and text alignment (left, right, center)
- Logging output to one or more files with support for overwriting or appending
Console output is handled via `Write-Host`, while logging uses `New-Item`, `Set-Content`, and `Add-Content`.
Multi-color support works by accepting an array of strings (`Text`) and matching each entry with optional arrays
of `ForegroundColor` and `BackgroundColor`. The value `Default` can be passed to use the terminal’s current color settings.
.PARAMETER Text
The text to display or log. Can be an array of multiple strings.
.PARAMETER ForegroundColor
Array of foreground colors corresponding to the text array.
.PARAMETER BackgroundColor
Array of background colors corresponding to the text array.
.PARAMETER ForegroundColorDefault
Fallback color used if ForegroundColor is not specified or set to Default.
.PARAMETER BackgroundColorDefault
Fallback color used if BackgroundColor is not specified or set to Default.
.PARAMETER PreLine
Number of blank lines to add before the text.
.PARAMETER PreSpace
Number of spaces to add before the text.
.PARAMETER PreTab
Number of tabs to add before the text.
.PARAMETER TimeStamp
Adds a timestamp before the text.
.PARAMETER TimeStampFormat
Format used for the timestamp.
.PARAMETER ClearHost
Clears the console screen before printing.
.PARAMETER PostSpace
Number of spaces to add after the text.
.PARAMETER PostTab
Number of tabs to add after the text.
.PARAMETER PostLine
Number of blank lines to add after the text.
.PARAMETER NoNewLine
Suppress newline at the end of the text.
.PARAMETER PadLeft
Pads the string to the left up to this number of characters.
.PARAMETER PadRight
Pads the string to the right up to this number of characters.
.PARAMETER PadCenter
Centers the string in a field of this width.
.PARAMETER LogFile
Array of file paths to log output to.
.PARAMETER LogOnly
If specified, skips console output and writes only to the log file.
.PARAMETER Overwrite
If specified and the file does not exist, it creates a new one. If the file exists, it overwrites it.
.EXAMPLE
Write-Out -Text "Clear all text on console" -Clearhost
.EXAMPLE
Write-Out -Text "Start with Red Text ","Then Switch to Blue Text ","Now Magenta" -ForegroundColor Red,Blue,Magenta
.EXAMPLE
Write-Out -Text "White on Black ","Black on White ","Dark Cyan on Cyan ","Yellow on Green ","Default Color" -ForegroundColor White,Black,DarkCyan,Yellow -BackgroundColor Black,White,Cyan,Green
.EXAMPLE
Write-Out -Text "Make this"," entire line"," the same color by setting defaults" -ForegroundColorDefault Yellow -BackgroundColorDefault Magenta
.EXAMPLE
Write-Out -Text "Add a blank line and two tabs ","before ","my text" -ForegroundColor Green,Cyan,White -PreLine 1 -PreTab 2
.EXAMPLE
Write-Out -Text "Add two blank ","lines ","after my text" -ForegroundColor White,Green,White -PostLine 2
.EXAMPLE
Write-Out -Text "Add 3 spaces before my text" -ForegroundColor Gray -Prespace 3
.EXAMPLE
Write-Out -Text "White text and a tab after" -ForegroundColor White -NoNewLine -PostTab 1
Write-Out -Text "Black text on Yellow ","and then back to white" -ForegroundColor Black,White -BackgroundColor Yellow
.EXAMPLE
Write-Out -Text "An easy way to ","highlight ","text in the middle" -BackgroundColor Default,Yellow
.EXAMPLE
Write-Out -Text "You can even add a ","time stamp ","before your output" -ForegroundColor White,Green,White -TimeStamp -PreLine 3
.EXAMPLE
Write-Out -Text "You can change the ","time stamp format" -ForegroundColor White,Yellow -TimeStamp -TimeStampFormat "dd-MM-yyy HH:mm" -PreLine 1 -PostLine 1
.EXAMPLE
Write-Out -Text "An"," Error"," occurred let's write overwrite/create a new log file" -ForegroundColor White,Red,White -TimeStamp -LogFile "script.log" -Overwrite
.EXAMPLE
Write-Out -Text "Now you can ","Append ","this line to your log file" -ForegroundColor Cyan,Magenta -TimeStamp -LogFile "script.log"
.EXAMPLE
Write-Out -Text "You can write this line to two log files now" -LogFile "script.log","second.txt"
.EXAMPLE
Write-Out -Text "Pad Right:", "20 Spaces" -ForegroundColor Yellow,White -PadRight 20
.EXAMPLE
Write-Out -Text "Pad Left:", "10 Spaces" -ForegroundColor Cyan,Green -PadLeft 10
.EXAMPLE
Write-Out -Text "Pad Center 30 Spaces" -ForegroundColor Green -PadCenter 30
.EXAMPLE
Write-Out -Text "Padding is useful to lineup text like a table" -ForegroundColor Gray -PreLine 1
Write-Out -Text "Name:", "Size:", "Date:" -ForegroundColor Yellow,Green,Cyan -PadRight 20
Write-Out -Text "windows.iso", "1.0 GB", "1/1/1995" -ForegroundColor Yellow,Green,Cyan -PadRight 20
Write-Out -Text "text.txt", "3.5 MB", "8/16/2024" -ForegroundColor Yellow,Green,Cyan -PadRight 20
.INPUTS
[string[]] Text can be piped or passed as an argument.
.OUTPUTS
None. This function writes to the console and/or log files.
.NOTES
Author: Brian Steinmeyer
URL: http://sigkillit.com/
Created: 5/23/2023
Requires: PowerShell 5.1 or higher
Version 1.6
- Rewrote code for max efficiency and speed
- Added PadLeft, PadRight, and PadCenter Parameters
Version 1.5
- Removed Unnecessary loop in 1.4 for log files since Test-Path, New-Item, Set-Content, and Add-Content all support -Path as string array
Version 1.4
- Changed LogFile from a String to String Array so you can log the same line to multiple files or provide a single value so it' backwards compatible.
Version 1.3
- Added LogOnly Option for only writing text to a log file
Version 1.2
- Set Text, ForegroundColor, and BackgroundColor to default value of @() to fix errors checking counts in some circumstances
- Fixed an issue where ForegroundColorDefault and BackgroundColorDefault were not working properly in some circumstances
- Added Requires -Version 4.0
Version 1.1
- Completely rewrote the "Main Text" section
- Added "Default" as a color option, which allows you to use the default values for foreground/background
- Useful when you want to specify a backgroundcolor in certain parts of a line like the middle
- Ex: Write-Out -Text "How to ","highlight ","the middle text" -BackgroundColor Default,Yellow,Default
Version: 1.0
- Initial Creation inspired by PSWriteColor (https://github.com/EvotecIT/PSWriteColor)
- Improved upon by switching Foreground and Background Colors to default values if colors are not specifid for all strings.
Will also ignore extra colors if more colors are specified than strings specified.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[string[]]$Text = @(),
[Parameter(Mandatory = $false)]
[ValidateSet("Default","Black","DarkBlue","DarkGreen","DarkCyan","DarkRed","DarkMagenta","DarkYellow","Gray","DarkGray","Blue","Green","Cyan","Red","Magenta","Yellow","White")]
[string[]]$ForegroundColor = @(),
[Parameter(Mandatory = $false)]
[ValidateSet("Default","Black","DarkBlue","DarkGreen","DarkCyan","DarkRed","DarkMagenta","DarkYellow","Gray","DarkGray","Blue","Green","Cyan","Red","Magenta","Yellow","White")]
[string[]]$BackgroundColor = @(),
[Parameter(Mandatory = $false)]
[ValidateSet("Default","Black","DarkBlue","DarkGreen","DarkCyan","DarkRed","DarkMagenta","DarkYellow","Gray","DarkGray","Blue","Green","Cyan","Red","Magenta","Yellow","White")]
[string]$ForegroundColorDefault = [ConsoleColor]::White,
[Parameter(Mandatory = $false)]
[ValidateSet("Default","Black","DarkBlue","DarkGreen","DarkCyan","DarkRed","DarkMagenta","DarkYellow","Gray","DarkGray","Blue","Green","Cyan","Red","Magenta","Yellow","White")]
[string]$BackgroundColorDefault = "Default",
[Parameter(Mandatory = $false)]
[int]$PreLine = 0,
[Parameter(Mandatory = $false)]
[int]$PreSpace = 0,
[Parameter(Mandatory = $false)]
[int]$PreTab = 0,
[Parameter(Mandatory = $false)]
[switch]$TimeStamp,
[Parameter(Mandatory = $false)]
[string]$TimeStampFormat = 'yyyy-MM-dd HH:mm:ss',
[Parameter(Mandatory = $false)]
[switch]$ClearHost,
[Parameter(Mandatory = $false)]
[int]$PostSpace = 0,
[Parameter(Mandatory = $false)]
[int]$PostTab = 0,
[Parameter(Mandatory = $false)]
[int]$PostLine = 0,
[Parameter(Mandatory = $false)]
[switch]$NoNewLine = $false,
[Parameter(Mandatory = $false)]
[int]$PadLeft = 0,
[Parameter(Mandatory = $false)]
[int]$PadRight = 0,
[Parameter(Mandatory = $false)]
[int]$PadCenter = 0,
[Parameter(Mandatory = $false)]
[string[]]$LogFile = @(),
[Parameter(Mandatory = $false)]
[switch]$LogOnly = $false,
[Parameter(Mandatory = $false)]
[switch]$Overwrite = $false
)
begin {
function Pad-Center {
param (
[string]$Text,
[int]$Width
)
$totalPadding = $Width - $Text.Length
if ($totalPadding -le 0) { return $Text }
$padLeft = [math]::Floor($totalPadding / 2)
$padRight = $totalPadding - $padLeft
return (' ' * $padLeft) + $Text + (' ' * $padRight)
}
if ($ClearHost) { Clear-Host }
$prefixBuilder = [System.Text.StringBuilder]::new()
if ($PreLine) { $null = $prefixBuilder.Append("`n" * $PreLine) }
if ($PreSpace) { $null = $prefixBuilder.Append(' ' * $PreSpace) }
if ($PreTab) { $null = $prefixBuilder.Append("`t" * $PreTab) }
if ($TimeStamp) { $null = $prefixBuilder.Append("[$([datetime]::Now.ToString($TimeStampFormat))]") }
$prefix = $prefixBuilder.ToString()
$postfixBuilder = [System.Text.StringBuilder]::new()
if ($PostSpace) { $null = $postfixBuilder.Append(' ' * $PostSpace) }
if ($PostTab) { $null = $postfixBuilder.Append("`t" * $PostTab) }
if ($PostLine) { $null = $postfixBuilder.Append("`n" * $PostLine) }
$postfix = $postfixBuilder.ToString()
}
process {
if (-not $LogOnly) {
if ($prefix) { Write-Host -NoNewline $prefix }
$currentFG = $null
$currentBG = $null
$buffer = [System.Text.StringBuilder]::new()
for ($i = 0; $i -lt $Text.Count; $i++) {
$txt = $Text[$i]
if ($PadRight -gt 0 -and $txt.Length -lt $PadRight) { $txt = $txt.PadRight($PadRight) }
if ($PadLeft -gt 0 -and $txt.Length -lt $PadLeft) { $txt = $txt.PadLeft($PadLeft) }
if ($PadCenter -gt 0 -and $txt.Length -lt $PadCenter) { $txt = Pad-Center -Text $txt -Width $PadCenter }
$fg = if ($ForegroundColor.Count -gt $i) { $ForegroundColor[$i] } else { $ForegroundColorDefault }
$bg = if ($BackgroundColor.Count -gt $i) { $BackgroundColor[$i] } else { $BackgroundColorDefault }
if ($buffer.Length -gt 0 -and ($fg -ne $currentFG -or $bg -ne $currentBG)) {
if ($currentBG -eq 'Default') {
Write-Host -NoNewline $buffer.ToString() -ForegroundColor $currentFG
} else {
Write-Host -NoNewline $buffer.ToString() -ForegroundColor $currentFG -BackgroundColor $currentBG
}
$buffer.Clear() | Out-Null
}
$null = $buffer.Append($txt)
$currentFG = $fg
$currentBG = $bg
}
if ($buffer.Length -gt 0) {
if ($currentBG -eq 'Default') {
Write-Host -NoNewline $buffer.ToString() -ForegroundColor $currentFG
} else {
Write-Host -NoNewline $buffer.ToString() -ForegroundColor $currentFG -BackgroundColor $currentBG
}
}
if ($postfix) { Write-Host -NoNewline $postfix }
if (-not $NoNewLine) { Write-Host }
}
if ($LogFile.Count -gt 0) {
$logLine = $prefix + ($Text -join '') + $postfix
foreach ($file in $LogFile) {
if (-not (Test-Path $file)) {
#New-Item -Path $file -ItemType File -Force -Value ($logLine + ($NoNewLine ? '' : "`r`n")) | Out-Null
if ($NoNewLine) {
$logValue = $logLine
} else {
$logValue = "$logLine`r`n"
}
New-Item -Path $file -ItemType File -Force -Value $logValue | Out-Null
} elseif ($Overwrite) {
Set-Content -Path $file -Value $logLine -NoNewline:$NoNewLine
} else {
try {
Add-Content -Path $file -Value $logLine -NoNewline:$NoNewLine -ErrorAction Stop
} catch {
Start-Sleep -Seconds 3
Add-Content -Path $file -Value $logLine -NoNewline:$NoNewLine
}
}
}
}
}
end {
#Nothing to Do
}
}