SCRIPT LIBRARY · POWERSHELL
Getting the Console Width in PowerShell (Even When There Isn't One)
A width check that works in a real console, in VS Code, and in scheduled tasks and CI runners where there's no window to measure.
- What it does
- Returns the console width in characters, trying the PowerShell host, then .NET's Console class, then the COLUMNS environment variable, then a default. Never throws.
- Requires
- Windows PowerShell 5.1 or PowerShell 7+
- No modules
- Permissions
- None.
- Runs on
- Windows, macOS, Linux, CI runners
- Tested
- Parse-checked and run in PowerShell 7.4 in a terminal, a non-interactive session and with only COLUMNS set
The one-liner is $Host.UI.RawUI.WindowSize.Width, and in a normal console it's all you need. I use it to draw separator lines, truncate long paths so a status line doesn't wrap, and decide whether a table fits or I should switch to a list.
Then you put that script in a scheduled task, or run it over remoting, or in a CI pipeline, and it falls apart. There's no window. Depending on the host, WindowSize comes back empty or zero, and [Console]::WindowWidth throws an IOException because there's no console handle to ask. A cosmetic helper takes down the whole script, which is a silly way for a job to fail at 2 a.m.
This version tries each source in turn, catches the failures, and falls back to a sensible default. With -Detailed it also tells you where the number came from, which is handy when you're trying to figure out why the output looks different in the pipeline than on your machine.
<#
.SYNOPSIS
Returns the width of the console window in characters, with a sensible fallback when
there isn't a real console to measure.
.DESCRIPTION
Tries the PowerShell host first ($Host.UI.RawUI.WindowSize), then .NET ([Console]::WindowWidth),
then the COLUMNS environment variable, and finally a default. Scheduled tasks, remoting
sessions, CI runners and redirected output often have no window at all, and the first two
can return 0, $null or throw. This never throws; it just tells you where the number came from.
.PARAMETER Default
Width to use when nothing can be measured. Default: 120.
.PARAMETER Detailed
Return an object with the width, where it came from, and whether output is redirected,
instead of just the number.
.EXAMPLE
.\Get-ConsoleWidth.ps1
.EXAMPLE
'-' * ((.\Get-ConsoleWidth.ps1) - 1)
.EXAMPLE
.\Get-ConsoleWidth.ps1 -Detailed
#>
[CmdletBinding()]
[OutputType([int])]
param(
[ValidateRange(20, 1000)]
[int]$Default = 120,
[switch]$Detailed
)
$width = 0
$source = $null
# 1. The PowerShell host. Right for the real console, Windows Terminal, VS Code and ISE.
try {
$w = $Host.UI.RawUI.WindowSize.Width
if ($w -gt 0) { $width = $w; $source = 'Host.UI.RawUI' }
}
catch { Write-Verbose "RawUI unavailable in host '$($Host.Name)': $($_.Exception.Message)" }
# 2. .NET's view of the console. Throws when there's no console handle.
if (-not $width) {
try {
$w = [Console]::WindowWidth
if ($w -gt 0) { $width = $w; $source = 'Console' }
}
catch { Write-Verbose "[Console]::WindowWidth unavailable: $($_.Exception.Message)" }
}
# 3. Many terminals and CI systems export COLUMNS.
if (-not $width -and $env:COLUMNS -match '^\d+$' -and [int]$env:COLUMNS -gt 0) {
$width = [int]$env:COLUMNS
$source = 'COLUMNS'
}
# 4. Give up gracefully.
if (-not $width) {
$width = $Default
$source = 'Default'
}
if ($Detailed) {
$redirected = try { [Console]::IsOutputRedirected } catch { $null }
[pscustomobject]@{
Width = $width
Source = $source
HostName = $Host.Name
OutputRedirected = $redirected
}
}
else {
$width
}
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-Default | int | 120 | The width to use when nothing can be measured. |
-Detailed | switch | — | Return an object with the width, its source, the host name and whether output is redirected, instead of just the number. |
Run it
Just the number.
.\Get-ConsoleWidth.ps1A separator line that fits the window exactly (minus one, so it doesn't wrap in older consoles).
'-' * ((.\Get-ConsoleWidth.ps1) - 1)Where did the number come from?
.\Get-ConsoleWidth.ps1 -DetailedMake sure wide tables aren't cut off in a log file.
Get-Process | Format-Table -AutoSize | Out-String -Width ([Math]::Max(200, (.\Get-ConsoleWidth.ps1)))What you'll see
PS> .\Get-ConsoleWidth.ps1
142
PS> .\Get-ConsoleWidth.ps1 -Detailed
Width Source HostName OutputRedirected
----- ------ -------- ----------------
142 Host.UI.RawUI ConsoleHost False
(and in a scheduled task with no console)
Width Source HostName OutputRedirected
----- ------ -------- ----------------
120 Default ConsoleHost True
How it works
- Ask the PowerShell host.
$Host.UI.RawUI.WindowSize.Widthis the right answer in the classic console, Windows Terminal, VS Code's integrated terminal and the ISE. If it throws or comes back as zero, move on. - Ask .NET.
[Console]::WindowWidthtalks to the console directly. It throws when there's no console attached, so it's wrapped intry. - Check
COLUMNS. Many terminals and CI systems set this environment variable, so it's a decent hint when the first two fail. - Fall back. If nothing works, return
-Default(120 unless you say otherwise). The script never throws, which is the whole point.
With -Detailed, you also get [Console]::IsOutputRedirected, so you can tell "the window is narrow" apart from "there is no window."
Take it further
- Build a truncate helper.
if ($text.Length -gt $w) { $text.Substring(0, $w - 4) + '...' }keeps progress lines on one row, which makes a long-running script far easier to watch. - Pick table or list automatically. Measure the table with
Out-String, and if the widest line is wider than the console, useFormat-Listinstead. - Use it in a profile. A prompt function that shortens the current path when the window is narrow is a nice small quality-of-life fix.
Things that'll trip you up
- Window size is not buffer size. $Host.UI.RawUI.BufferSize.Width is how wide the scrollback buffer is, which in the old Windows console could be much wider than what you can see. For "will this line wrap on screen," you want WindowSize.
- Resizing doesn't update your variable. The width is read once, when you call the script. If someone resizes the window during a long run, call it again right before you draw anything that depends on it.
- Redirected output has no width. When output is going to a file or another program, there's no meaningful width at all. That's what -Default is for, and it's why Out-String -Width is the right tool for log files.
- ISE is its own thing. The PowerShell ISE reports a width from its output pane, and [Console] calls there don't reflect it. The host check runs first for exactly this reason.