PowerShell String Padding and Alignment for Reports

PowerShell String Padding and Alignment for Reports

PowerShell Tips Editor 3 min read
PowerShell String Padding and Alignment for Reports

When you build a custom console report without Format-Table, columns drift into misalignment the moment any value is a different length. PowerShell string padding methods — PadRight, PadLeft, and the -f format operator — give you pixel-perfect column alignment for any custom report. This post shows every technique for building readable console output with consistently aligned text.

Quick Answer / TL;DR

Use $string.PadRight(20) to left-align in a 20-char column, $string.PadLeft(10) to right-align numbers, and '{0,-20}{1,10}' -f $name, $count for format-string-based alignment in a single expression.

PadRight for Left-Aligned Columns

PadRight(totalWidth) appends spaces to the right of a string until it reaches the specified total length. If the string is already longer than totalWidth, it is returned unchanged — no truncation. This left-aligns text in a fixed-width column.

# Left-aligned column using PadRight
$services = Get-Service | Select-Object -First 5

foreach ($svc in $services) {
    $name   = $svc.Name.PadRight(25)
    $status = $svc.Status.ToString().PadRight(10)
    Write-Host "$name $status $($svc.StartType)"
}
AJRouter                  Stopped    Manual
ALG                       Stopped    Manual
AppIDSvc                  Stopped    Manual
AppMgmt                   Stopped    Manual
AppReadiness              Stopped    Manual

PadLeft for Right-Aligned Numbers

PadLeft(totalWidth) prepends spaces to left-align the string right — ideal for numbers so decimal points and digit positions line up vertically. Combine with .ToString() to convert numbers before padding.

# Right-aligned numbers using PadLeft
$processes = Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 5

foreach ($p in $processes) {
    $name = $p.Name.PadRight(20)
    $mem  = ([math]::Round($p.WorkingSet / 1MB, 1)).ToString('F1').PadLeft(8)
    $cpu  = ([math]::Round($p.CPU, 1)).ToString('F1').PadLeft(8)
    Write-Host "$name $mem MB  $cpu s"
}

Format with -f and Width Specifiers

The -f format operator provides format strings with built-in width and alignment. {0,-20} left-aligns argument 0 in a 20-character field; {1,10} right-aligns argument 1 in a 10-character field. A negative width means left-align; positive means right-align. This is the most concise syntax for complex multi-column output.

# Format operator with width specifiers
$header = '{0,-25} {1,-12} {2,10} {3,12}' -f 'Name','Status','Handles','WorkingSet MB'
$divider = '-' * 62

Write-Host $header
Write-Host $divider

Get-Process | Select-Object -First 10 | ForEach-Object {
    $line = '{0,-25} {1,-12} {2,10} {3,12:F1}' -f `
        $_.Name,
        $_.Responding,
        $_.Handles,
        ($_.WorkingSet / 1MB)
    Write-Host $line
}

Build a Console Table Function

A reusable function that accepts objects and column definitions, automatically measures the widest value per column, and renders an aligned table. This replaces Format-Table for cases where you need full control over the output.

function Write-ConsoleTable {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object[]]$InputObject,

        [Parameter(Mandatory)]
        [string[]]$Properties,

        [int]$MinWidth = 5
    )

    begin { $rows = @() }
    process { $rows += $InputObject }
    end {
        # Calculate column widths
        $widths = @{}
        foreach ($prop in $Properties) {
            $maxLen = $prop.Length
            foreach ($row in $rows) {
                $val = "$($row.$prop)"
                if ($val.Length -gt $maxLen) { $maxLen = $val.Length }
            }
            $widths[$prop] = [math]::Max($maxLen, $MinWidth)
        }

        # Print header
        $header = ($Properties | ForEach-Object { $_.PadRight($widths[$_]) }) -join '  '
        Write-Host $header
        Write-Host ('-' * $header.Length)

        # Print rows
        foreach ($row in $rows) {
            $line = ($Properties | ForEach-Object { "$($row.$_)".PadRight($widths[$_]) }) -join '  '
            Write-Host $line
        }
    }
}

Get-Service | Select-Object -First 8 |
    Write-ConsoleTable -Properties Name, Status, StartType

Handle Multi-Line Cell Content

When a value contains newlines, padding alone does not produce aligned multi-line cells. The common approach is to truncate to a single line or replace newlines with a separator character before padding.

# Replace newlines before padding
$description = "Line one`nLine two`nLine three"
$singleLine  = $description -replace "`n", ' | '
$padded      = $singleLine.PadRight(50)

Write-Host "[$padded]"
# Output: [Line one | Line two | Line three          ]

Truncate Long Values with Substring

When values exceed the column width, truncate with an ellipsis to keep columns aligned. Always check the string length before calling Substring to avoid index-out-of-range errors.

# Safe truncation with ellipsis
function Format-TruncatedString {
    param([string]$Value, [int]$MaxLength = 30)

    if ($Value.Length -le $MaxLength) {
        return $Value.PadRight($MaxLength)
    }
    # Truncate and add ellipsis
    return $Value.Substring(0, $MaxLength - 3) + '...'
}

$services = Get-Service
foreach ($svc in $services | Select-Object -First 5) {
    $name    = Format-TruncatedString $svc.DisplayName -MaxLength 40
    $status  = $svc.Status.ToString().PadRight(10)
    Write-Host "$name $status"
}

Common Errors and Fixes

  • PadRight adds spaces that look invisible — check length before assuming. If output does not look aligned, the values may contain hidden characters (non-breaking spaces, tab characters) that look like spaces but occupy different widths. Use $string.Length and [int][char]$string[index] to inspect problematic characters.
  • Unicode characters throw off single-char width assumptions. Characters like emoji, Chinese/Japanese/Korean characters, and some special symbols are “wide” characters that occupy two columns in most terminals. PowerShell’s .Length counts code points, not display columns. For terminal output with Unicode content, test with your target terminal emulator.

Related Cmdlets / See Also

Wrapping Up

PadRight left-aligns text, PadLeft right-aligns numbers, and -f with width specifiers handles complex multi-column layouts in one expression. For complete custom tables, the Write-ConsoleTable function automatically sizes columns to fit the data. Truncate long values with a safe ellipsis helper to maintain alignment regardless of value length.

Send-Item -To