In this post
I got triggered by a question on our MVP maillist, about using HashTables to make Custom columns with Select Object, a technique I use often
'm glad that you don't haev to remember label or name now, but it has always perplexed me why you ever name name,expression at all given that in all these cases you don't have hashtables with many objects, but rather an array of hashtables each with just the single (well two) items in them
hello"| select-Object length,@{Name="onetoten";Expression = {1..10}},@{Name="thelength";Expression = {$_.length}}
why not just
"hello"| select-Object length,@{onetoten = {1..10};thelength= {$_.length}}
Jeffrey Snover Provided the following list for V2 with his answer ( The only place I found a list before for PowerShell V1.0 is is the "MSH User guide "(from the Beta version, seems to be replaced by the primer where I can't find it) hence I will paste the whole list here for reference)
We can take more than just name and expression:
· Select-Object / -Property:
o Expression: string / scriptblock
o Name/Label: string
· Group-Object / -Property:
o Expression: string / scriptblock
· Sort-Object / -Property
o Expression: string / scriptblock
o Ascending: bool
o Descending: bool
· Format-Wide / -Property
o Expression: string / scriptblock
o FormatString: string
· Format-Custom / -Property
o Expression: string / scriptblock
o Depth: int
· Format-List / -Property
o Expression: string / scriptblock
o FormatString: string
o Label/Name: string
· Format-Table / -Property
o Expression: string / scriptblock
o FormatString: string
o Label/Name: string
o Width: int
o Alignment: string (limited to "left", "center", "right")
· Format-XXX / -GroupBy
o Expression: string / scriptblock
o FormatString: string
o Label/Name: string
As you can see from the list in the answer is that not only the select-object cmdlet takes a Hashtable as a custom property, but much more cmdlets do support this method amongst them all the Format Cmdlets.This technique can be very powerfull, but as I noted above, it is very hard to find information about this functionaly, or find a list of the possible options you have for the HashTable as in this list above.
Next to the use with Select-Object, the most usefull use of HashTables like this is for the Format-Table cmdlet where it provides a very powerfull way to customize and format the output by creating Custom Columns, and as you can see in the list above it is the Cmdlet that has the most options too.
So I thought that it would make a good blog topic .and I figured that next to that making a function for this work, would make a good example to show the use of the advanced function possibilities in parameter handling in PowerShell V2 CTP3 .
But as I did not have Powershell V2 CTP3 installed on the OS I'm currently running, I decided to start out with a PowerShell V1 example to show the Possibilities of Format-Table with Custom Columns and will show how we can improve this function in PowerShell V2 in the next post
The Basics :
we can use scriptblocks as parameters often in PowerShell and this is a very powerfull concept see for example /\/\o\/\/ PowerShelled: PowerShell : Advanced renaming of files
in a select command we can also use scriptblocks instead of property to select , see for example the following post on my old blog :
/\/\o\/\/ PowerShelled: Report MP3 count and size by User from MSH
but as the complete scripttext becomes the property name, this is very hard to work with, we can solve that by creating a HashTable that has a value for the name and a value for the expression
, so we can give the scriptproperty a name.
PS E:\PowerShell> dir | select name
Name
----
PowerShellAnalyzer
computers.txt
..
PS E:\PowerShell> dir | select {$_.name.toupper()}
$_.name.toupper()
-----------------
POWERSHELLANALYZER
COMPUTERS.TXT
..
PS E:\PowerShell> dir | select @{n='Name';e={$_.name.toupper()}}
Name
----
POWERSHELLANALYZER
COMPUTERS.TXT
..
for more usage examples and information how to preprepare them see also this post (ignore the topic, half way the post you can find a complete walktrough, explaining how using hashtables with select works and how to create the HashTables in front and store them in variables):
/\/\o\/\/ PowerShelled: PowerShell and Active Directory Part 5
Using HashTables with format-table
The first caveat in PowerShell V1 is that with format-table the Name property is not supported but the Label property is used instead, this is very confusing and is solved in PowerShell V2 by supporting both Name as Label.
PS E:\PowerShell> dir | ft @{n='Name';e={$_.name.toupper()}}
Format-Table : Illegal key n
At line:1 char:9
+ dir | ft <<<< @{n='Name';e={$_.name.toupper()}}
PS E:\PowerShell> dir | ft @{label='Name';e={$_.name.toupper()}}
Name
----
POWERSHELLANALYZER
COMPUTERS.TXT
As we have a lot of properties this can get very long and cryptic but we can make the hashTables in front and store them in a variable as shown in detail in the 2th post about select /\/\o\/\/ PowerShelled: PowerShell and Active Directory Part 5:
PS E:\PowerShell> $name = @{label='Name';e={$_.name.toupper()}}
PS E:\PowerShell> dir | ft $name
Name
----
POWERSHELLANALYZER
COMPUTERS.TXT
But as Format-Object knows more properties I made a function to make even easier to make the Custom Columns
New-CustomColumn
# Function New-CustomColumn for PowerShell V1.0
#
# Helper function to create Custom Columns for select or format cmdlets
# for more info and examples see :
# http://thepowershellguy.com/blogs/posh/archive/2009/01/26/new-customcolumn-function-powershell-v1-0.aspx
#
# /\/\o\/\/ 2008
# http://thePowerShellGuy.com
Function New-CustomColumn {
PARAM (
$Expression,
$name,
$label,
$FormatString,
[int]$Width,
$Alignment
)
$column = @{}
if ($Expression){
$Column.Expression = $Expression
} else {
throw "Expression is mandatory"
}
if ($Name) {$Column.Name = $name}
if ($Label) {$Column.Label = $Label}
if ($FormatString) {$Column.FormatString = $FormatString}
if ($Width) {$Column.Width = $Width}
if ($Alignment) {$Column.Alignment = $Alignment}
$Column.psobject.baseobject
}
Using the function above to create the HashTables for the Columns, I will show how we can make a completly customized output and cover the extra options we have with Format-Table, by changing the output of the dir command in PowerShell to mimic the DOS dir command.
PS E:\PowerShell> new-CustomColumn -label Size -expression {$_.Length}
Name Value
---- -----
Label Size
Expression $_.Length
PS E:\PowerShell> $size = new-CustomColumn -label Size -expression {$_.Length}
PS E:\PowerShell> dir | ft name,$size
Name Size
---- ----
PowerShellAnalyzer
computers.txt 25
getworldtime.ps1 1181
HttpRest.ps1 17057
podiobooks.txt 186
poker.ps1 21340
PowerShellAnalyzer.zip 7439208
prun.txt 4048
psghost.zip 3312
spaghetticode-powershell.mp3 19235007
StartPage.vbs 537
in the example above we started with only a label to rename a property (Note that we need to use $_.Length now instead of just the name of the property , in the scriptblock the $_ variable will contain the original Object that is passed into the select command)
PS E:\PowerShell> $modified = new-CustomColumn -l Modified -expression {$_.LastWriteTime} -formatString "yyyy-MM-dd HH:mm" -Width 20
PS E:\PowerShell> dir | ft $modified,$size,name
Modified Size Name
-------- ---- ----
2008-12-20 10:50 PowerShellAnalyzer
2008-12-16 21:28 25 computers.txt
2008-12-31 00:26 1181 getworldtime.ps1
2008-12-18 22:30 17057 HttpRest.ps1
2008-12-15 09:47 186 podiobooks.txt
2009-01-01 23:09 21340 poker.ps1
2008-12-20 10:50 7439208 PowerShellAnalyzer.zip
2009-01-01 23:09 4048 prun.txt
2008-12-18 23:38 3312 psghost.zip
2008-12-20 08:49 19235007 spaghetticode-powershell.mp3
2008-12-18 12:16 537 StartPage.vbs
In the next custom column we add "Modified" we used a new option -FormatString to not only rename the LastWriteTime property to Modified but also to display the date in a custom format (year-Month-Day time) and next to that we also set the Modified column to a fixed Width.
In the Following example I will show that as the Expression takes a scriptblock, we can embedd a complete multiline PowerShell script here, giving almost unlimited posibilities, in this case I use an if statement in the $type column to check the type of Object and if it is a directory I return <DIR> to mimic the output of the dos dir command :
PS E:\PowerShell> cmd /c dir
Volume in drive E has no label.
Volume Serial Number is DCE7-A82D
Directory of E:\PowerShell
01-01-2009 23:09 <DIR> .
01-01-2009 23:09 <DIR> ..
16-12-2008 21:28 25 computers.txt
31-12-2008 00:26 1.181 getworldtime.ps1
18-12-2008 22:30 17.057 HttpRest.ps1
15-12-2008 09:47 186 podiobooks.txt
01-01-2009 23:09 21.340 poker.ps1
20-12-2008 10:50 <DIR> PowerShellAnalyzer
20-12-2008 10:50 7.439.208 PowerShellAnalyzer.zip
01-01-2009 23:09 4.048 prun.txt
18-12-2008 23:38 3.312 psghost.zip
20-12-2008 08:49 19.235.007 spaghetticode-powershell.mp3
18-12-2008 12:16 537 StartPage.vbs
10 File(s) 26.721.901 bytes
3 Dir(s) 3.077.910.528 bytes free
To get the <DIR> before the Directories as we see in the output from the DOS dir command above
PS E:\PowerShell> $type = new-CustomColumn -label type -Width 5 -expression {
>> if($_.gettype().fullname -eq 'System.IO.DirectoryInfo'){
>> '<dir>'
>> }
>> }
>>
PS E:\PowerShell> dir | format-table $modified,$type,$size,$file
Modified type Size File
-------- ---- ---- ----
2008-12-20 10:50 <dir> PowerShellAnalyzer
2008-12-16 21:28 25 computers.txt
2008-12-31 00:26 1181 getworldtime.ps1
2008-12-18 22:30 17057 HttpRest.ps1
2008-12-15 09:47 186 podiobooks.txt
2009-01-01 23:09 21340 poker.ps1
2008-12-20 10:50 7439208 PowerShellAnalyzer.zip
2009-01-01 23:09 4048 prun.txt
2008-12-18 23:38 3312 psghost.zip
2008-12-20 08:49 19235007 spaghetticode-powershell.mp3
2008-12-18 12:16 537 StartPage.vbs
Almost there only I'm not content with the Size yet, when we look at the output of dir in CMD.EXE we see that is looking better as it has thousand separators and it is right alligned, so lets see if we can do the same :
PS E:\PowerShell> $size = new-CustomColumn -label Size -expression {if($_.length){$_.length / 1kb} } -w 15 -Align 'Right' -f "#,0.## KB"
PS E:\PowerShell> dir | format-table $modified,$type,$size,$file
Modified type Size File
-------- ---- ---- ----
2008-12-20 10:50 <dir> PowerShellAnalyzer
2008-12-16 21:28 0,02 KB computers.txt
2008-12-31 00:26 1,15 KB getworldtime.ps1
2008-12-18 22:30 16,66 KB HttpRest.ps1
2008-12-15 09:47 0,18 KB podiobooks.txt
2009-01-01 23:09 20,84 KB poker.ps1
2008-12-20 10:50 7.264,85 KB PowerShellAnalyzer.zip
2009-01-01 23:09 3,95 KB prun.txt
2008-12-18 23:38 3,23 KB psghost.zip
2008-12-20 08:49 18.784,19 KB spaghetticode-powershell.mp3
2008-12-18 12:16 0,52 KB StartPage.vbs
I used the Alignment parameter to right align the Size, Converted the size into KB in the Expression and formatted it with 2 decimals, seperators and did place KB behind it.
And with that we covered all the options we can use in the HashTable for the Format-Table Cmdlet, below you find the complete code for the examples above :
$modified = new-CustomColumn -label Modified -expression {$_.LastWriteTime} -f "yyyy-MM-dd HH:mm" -w 20
$type = new-CustomColumn -label type -Width 5 -expression {
if($_.gettype().fullname -eq 'System.IO.DirectoryInfo'){
'<dir>'
}
}
$size = new-CustomColumn -label Size -expression {if($_.length){$_.length / 1kb} } -w 15 -Align 'Right' -f "0.00 KB"
dir | format-table $modified,$type,$size,$file
That is it for now, it has become a long post but I hope it was usefull, In next post I will show how you can improve on the function using the CTP3 build
Enjoy,
Greetings /\/\o\/\/