From time to time I use Format-Wide
to display list of some data. It works great but sometimes my eyes have to jump quite a long distance between the columns – that's because in some cases the order matters. Example:
[1]: 'The basic promise behind implicit remoting is that you can work '+
'with remote commands using local syntax.' -split ' ' | Format-Wide -force -prop {$_} -col 3
The basic promise
behind implicit remoting
is that you
can work with
remote commands using
local syntax.
In case you have to read the values in correct order, you will not feel comfortable, right?
So, check this:
[2]: 'The basic promise behind implicit remoting is that you can work '+
'with remote commands using local syntax.' -split ' ' | . Format-Columns -col 3
The is remote
basic that commands
promise you using
behind can local
implicit work syntax.
remoting with
This version reads much better. You just follow whole columns, one by one.
Code
In case you find it helpful, here is the code:
function Format-Columns {
################################################################
#.Synopsis
# Formats incoming data to columns.
#.Description
# It works similarly as Format-Wide but it works vertically. Format-Wide outputs
# the data row by row, but Format-Columns outputs them column by column.
#.Parameter Property
# Name of property to get from the object.
# It may be
# -- string - name of property.
# -- scriptblock
# -- hashtable with keys 'Expression' (value is string=property name or scriptblock)
# and 'FormatString' (used in -f operator)
#.Parameter Column
# Count of columns
#.Parameter Autosize
# Determines if count of columns is computed automatically.
#.Parameter MaxColumn
# Maximal count of columns if Autosize is specified
#.Parameter InputObject
# Data to display
#.Example
# PS> 1..150 | Format-Columns -Autosize
#.Example
# PS> Format-Columns -Col 3 -Input 1..130
#.Example
# PS> Get-Process | Format-Columns -prop @{Expression='Handles'; FormatString='{0:00000}'} -auto
#.Example
# PS> Get-Process | Format-Columns -prop {$_.Handles} -auto
#.Notes
# Name: Get-Columns
# Author: stej, http://twitter.com/stejcz
# Lastedit: 2010-01-14
# Version 0.2 - 2010-01-14
# - added MaxColumn
# - fixed bug - displaying collection of 1 item was incorrect
# Version 0.1 - 2010-01-06
################################################################
param(
[Parameter(Mandatory=$false,Position=0)][Object]$Property,
[Parameter()][switch]$Autosize,
[Parameter(Mandatory=$false)][int]$Column,
[Parameter(Mandatory=$false)][int]$MaxColumn,
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][PsObject[]]$InputObject
)
begin { $values = @() }
process { $values += $InputObject }
end {
function ProcessValues {
$ret = $values
$p = $Property
if ($p -is [Hashtable]) {
$exp = $p.Expression
if ($exp) {
if ($exp -is [string]) { $ret = $ret | % { $_.($exp) } }
elseif ($exp -is [scriptblock]) { $ret = $ret | % { & $exp $_} }
else { throw 'Invalid Expression value' }
}
if ($p.FormatString) {
if ($p.FormatString -is [string]) { $ret = $ret | % { $p.FormatString -f $_ } }
else { throw 'Invalid format string' }
}
}
elseif ($p -is [scriptblock]) { $ret = $ret | % { & $p $_} }
elseif ($p -is [string]) { $ret = $ret | % { $_.$p } }
elseif ($p -ne $null) { throw 'Invalid -property type' }
# in case there were some numbers, objects, etc., convert them to string
$ret | % { $_.ToString() }
}
function Base($i) { [Math]::Floor($i) }
function Max($i1, $i2) { [Math]::Max($i1, $i2) }
if (!$Column) { $Autosize = $true }
$values = ProcessValues
$valuesCount = @($values).Count
if ($valuesCount -eq 1) {
$values | Out-Host
return
}
# from some reason the console host doesn't use the last column and writes to new line
$consoleWidth = $host.ui.RawUI.maxWindowSize.Width -1;
$spaceWidthBetweenCols = 2
# get length of the longest string
$values | % -Begin { [int]$maxLength = -1 } -Process { $maxLength = Max $maxLength $_.Length }
# get count of columns if not provided
if ($Autosize) {
$Column = Max (Base ($consoleWidth/($maxLength+$spaceWidthBetweenCols))) 1
$remainingSpace = $consoleWidth - $Column*($maxLength+$spaceWidthBetweenCols);
if ($remainingSpace -ge $maxLength) {
$Column++
}
if ($MaxColumn -and $MaxColumn -lt $Column) {
Write-Debug "Columns corrected to $MaxColumn (original: $Column)"
$Column = $MaxColumn
}
}
$countOfRows = [Math]::Ceiling($valuesCount / $Column)
$maxPossibleLength = Base ($consoleWidth / $Column)
# cut too long values, considers count of columns and space between them
$values = $values | % {
if ($_.length -gt $maxPossibleLength) { $_.Remove($maxPossibleLength-2) + '..' }
else { $_ }
}
#add empty values so that the values fill rectangle (2 dim array) without space
if ($Column -gt 1) {
$values += (@('') * ($countOfRows*$Column - $valuesCount))
}
# in case there is only one item, make it array
$values = @($values)
<#
now we have values like this: 1, 2, 3, 4, 5, 6, 7, ''
and we want to display them like this:
1 3 5 7
2 4 6 ''
#>
$formatString = (1..$Column | %{"{$($_-1),-$maxPossibleLength}"}) -join ''
1..$countOfRows | % {
$r = $_-1
$line = @(1..$Column | % { $values[$r + ($_-1)*$countOfRows]} )
$formatString -f $line | Out-Host
}
}
}
At at the end one (almost) real example:
[3]: Get-Process |
Sort-Object Handles -desc |
Select-Object -first 30 |
Format-Columns -Property { '{0,-10} - {1,5}' -f $_.Name,$_.Handles} -auto
System - 2289 firefox - 690 taskeng - 451 MSASCui - 360
svchost - 1216 lsass - 672 cmdagent - 444 PCSuite - 347
explorer - 1122 svchost - 642 Jing - 441 procexp - 341
ScriptEditor - 870 Skype - 628 CompPtcVUI - 431 svchost - 333
powershell - 867 powershell - 565 svchost - 424 svchost - 328
csrss - 745 svchost - 528 svchost - 414 spoolsv - 312
csrss - 727 svchost - 500 svchost - 410
trillian - 708 upeksvr - 482 wlanext - 397
Download