Sunday, November 11, 2007

Formatting with PowerShell

One of the great features of PowerShell is that much formatting is done for free. For example, to find out the size of a particular file you could just enter:

PSH [D:\foo]: ls demo-format.txt

    Directory: Microsoft.PowerShell.Core\FileSystem::D:\foo

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        11/11/2007     12:26       2008 demo-format.txt

In most cases, that's good enough - you can see the file size is 2008 bytes long. But suppose you wanted to know how big the file was in GB, or to print it out in a nicely formatted layout. Using some of PowerShell's .NET underpinnings, this turns out to be easy to achieve with what .NET calls Composite Formatting.

With composite formatting, you get PowerShell to format one or  more values as a pretty string, and insert this string (or strings) into a  into a string known formally as a composite formatting string. The composite format string contains any text you specify, along one one or more place holders,  known as format items. PowerShell creates a result string that has the original text you specified along with string representations of the objects in the list.

An example composite format string might be:

"This is stuff{0} and more {1} stuff and more {2}"

This format string has three place holders. If you use PowerShell's -F string operator you can get PowerShell to format this string with some values, as follows:

"This is stuff{0} and more {1} stuff and more {2}" -f ",", "really cool", "excellent stuff"

What PowerShell does is to take the values to the right of the -f operator, and place them into the string as indicated by the placeholders {0}, {1}. and {2}. The first variable to the right is places into {0}, the second into {1}, and the third into {2}, with the result of:

This is stuff, and more really cool stuff and more excellent stuff

The format of the placeholders, however, can be more complex than just a simple index into the array of variables. The full syntax of the placeholder is:

{index[,alignment][:formatString]}

This allows you allows you to right and left justify the value into a fixed length number of characters, as well as to use the format string to format dates, phone numbers, etc.

Rather than drone on,  here is a demo script I've knocked up to demonstrate the formatting capabilities of PowerShell.

NB: This script needs Version 2 of PowerShell (although most of the features work OK in V2).

 

[0]PS D:\foo > #REQUIRES -Version 2
[1]PS D:\foo > #
[2]PS D:\foo > # First, simple formatting
[3]PS D:\foo > $num=123.45

[4]PS D:\foo > "`$num = $num"
$num = 123.45

[5]PS D:\foo > #
[6]PS D:\foo > # Show Conversion into *b
[7]PS D:\foo > #
[8]PS D:\foo > $num=1234567890123.12345

[9]PS D:\foo > $num/1kb
1205632705.19836

[10]PS D:\foo > $num/1mb
1177375.68867028

[11]PS D:\foo > $num/1gb
1149.78094596707

[12]PS D:\foo > $num/1tb
1.12283295504596

[13]PS D:\foo > $num/1pb
0.00109651655766207

[14]PS D:\foo > #
[15]PS D:\foo > #
[16]PS D:\foo > # Demo String Format and Replace
[17]PS D:\foo > #
[18]PS D:\foo > # simple substitutions
[19]PS D:\foo > "{0}    {1}  {2}"         -f "123", 123, 234
123    123  234

[20]PS D:\foo > # or
[21]PS D:\foo > $a=1; $b=2

[22]PS D:\foo > "{0} + {1} = {2}"         -f $a, $b, $a+$b
1 + 2 = 12

[23]PS D:\foo > #
[24]PS D:\foo > # NUMBERS
[25]PS D:\foo > #
[26]PS D:\foo > $number = 12345.457

[27]PS D:\foo > # right aligned number with two decimal places
[28]PS D:\foo > #
[29]PS D:\foo > "*****`+{0,20:n2}`+*****"  -f $number
*****+           12,345.46+*****

[30]PS D:\foo > # left aligned number with two decimal places
[31]PS D:\foo > #
[32]PS D:\foo > "*****`+{0,-20:n2}`+*****" -f $number
*****+12,345.46           +*****

[33]PS D:\foo > # left aligned number with four decimal places
[34]PS D:\foo > #
[35]PS D:\foo > "*****`+{0,-20:n4}`+*****" -f $number
*****+12,345.4570         +*****

[36]PS D:\foo > #
[37]PS D:\foo > # TIME AND DATE
[38]PS D:\foo > #
[39]PS D:\foo > $date=Get-Date

[40]PS D:\foo > #12-hour format (lower case hh)
[41]PS D:\foo > "{0:hh.mm.ss tt}  "       -f $date
12.26.32 PM

[42]PS D:\foo > # 24 hour time format (upper case HH)
[43]PS D:\foo > "{0:HH:mm:ss}"            -f $date
12:26:32

[44]PS D:\foo > # Date in default format
[45]PS D:\foo > "{0}         "            -f $date
11/11/2007 12:26:32

[46]PS D:\foo > # date in a custom short format
[47]PS D:\foo > "{0:MM dd yy}"            -f $date
11 11 07

[48]PS D:\foo > # date, "month year" format
[49]PS D:\foo > "{0:MMMM yyyy}"           -f $date
November 2007

[50]PS D:\foo > # or even
[51]PS D:\foo > "{0:yyyy-MM-dd}"          -f $date
2007-11-11

[52]PS D:\foo > #
[53]PS D:\foo > # CURRENCY
[54]PS D:\foo > # basic with 2 decimal places
[55]PS D:\foo > #
[56]PS D:\foo > $money = 12345.678

[57]PS D:\foo > "{0:c2}"                   -f $money
£12,345.68

[58]PS D:\foo > # right aligned currency with two decimal places
[59]PS D:\foo > #
[60]PS D:\foo > "*****`+{0,22:c2}`+*****"   -f $money
*****+            £12,345.68+*****

[61]PS D:\foo > #
[62]PS D:\foo > # PERCENTAGES
[63]PS D:\foo > #
[64]PS D:\foo > # percent with 2 decimal places
[65]PS D:\foo > $pct = 9/29

[66]PS D:\foo > "{0:p2}"                      -f $pct
31.03 %

[67]PS D:\foo > # percent with four decimal places
[68]PS D:\foo > "{0:p4}"                      -f $pct
31.0345 %

[69]PS D:\foo > #
[70]PS D:\foo > # PHONE NUMBERS
[71]PS D:\foo > #
[72]PS D:\foo > "{0:(###)(###) ####}"         -f 4255551212
(425)(555) 1212

[73]PS D:\foo > # UK phone
[74]PS D:\foo >  "{0:(##)(###) ######}"       -f 44118123456
(44)(118) 123456

[75]PS D:\foo > #
[76]PS D:\foo > # FANCY TRICKS
[77]PS D:\foo > #
[78]PS D:\foo > # create leading zeros
[79]PS D:\foo > 0..15 | % {"{0:0##}"          -f $_}
000
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015

[80]PS D:\foo > 0..15 | % {"{0:0####}"        -f $_}
00000
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015

[81]PS D:\foo > $str="This is number"

[82]PS D:\foo > # right aligned string
[83]PS D:\foo > 0..15 | % {"`+$str {0,10}`+"  -f $_}
+This is number          0+
+This is number          1+
+This is number          2+
+This is number          3+
+This is number          4+
+This is number          5+
+This is number          6+
+This is number          7+
+This is number          8+
+This is number          9+
+This is number         10+
+This is number         11+
+This is number         12+
+This is number         13+
+This is number         14+
+This is number         15+

[84]PS D:\foo > # Left Aligned string
[85]PS D:\foo > 0..15 | % {"`+$Str {0,-10}`+" -f $_}
+This is number 0         +
+This is number 1         +
+This is number 2         +
+This is number 3         +
+This is number 4         +
+This is number 5         +
+This is number 6         +
+This is number 7         +
+This is number 8         +
+This is number 9         +
+This is number 10        +
+This is number 11        +
+This is number 12        +
+This is number 13        +
+This is number 14        +
+This is number 15        +

[86]PS D:\foo > # right & left aligned string
[87]PS D:\foo > 0..15 | % {"*** `+{0,10}`+ ***** `+{0,-10}`+ ***"   -f $_}
*** +         0+ ***** +0         + ***
*** +         1+ ***** +1         + ***
*** +         2+ ***** +2         + ***
*** +         3+ ***** +3         + ***
*** +         4+ ***** +4         + ***
*** +         5+ ***** +5         + ***
*** +         6+ ***** +6         + ***
*** +         7+ ***** +7         + ***
*** +         8+ ***** +8         + ***
*** +         9+ ***** +9         + ***
*** +        10+ ***** +10        + ***
*** +        11+ ***** +11        + ***
*** +        12+ ***** +12        + ***
*** +        13+ ***** +13        + ***
*** +        14+ ***** +14        + ***
*** +        15+ ***** +15        + ***

2 comments:

wboaz said...

Some good examples here!

As someone still learning I wish you hadn't used the alias % in examples. It's confusing to read.

Adding some examples of how to format in HTML would also be cool.

Anonymous said...

Saw you were repeating asterisks a lot; a simpler way to do that is:

$('*'*[times-to-repeat])

example:
$stars = $('*'*5)
1..15|%{"{0}{1,10}`+ {0} `+{1,-10}{0}" -f $stars,$_}

Great stuff, though!