The Trouble with Format-* Commands in PowerShell
There are a handful of Format commands that can really help make your data easier to look at.
- Format-List (FL)
- Format-Custom (FC)
- Format-Table (FT)
- Format-Wide (FW)
I’ll commonly use either Format-List or Format-Table to display the data of an object in the console to see whats in there but I often times see people put this in their scripts or even worse, in the middle of a pipe.
Side Note: Another common mistake is for people to use write-host in a script, see this.
The problem with using the Format commands is that it is for displaying info and when it does this it converts your object to a special internal type object. Lets play with an IPAddress and see what we have.
$ip = [ipaddress] '192.168.1.1' $ip $ip | ft $ip | gm $ip | ft | gm
So if you take a look at the output you’ll see a couple of things,
- The default view of an IP object is a List view. Some objects have default definitions for views and are stored in format files here C:\Windows\System32\WindowsPowerShell\v1.0 (NEVER EDIT THIS FILE), if you want to know more about these format files comment on this post and I’ll do a write up. If a default view isnt defined (which most arent) the rule is, more than 4 properties make it a list, 4 or less make it a table.)
- If we ask for it as a table it will squeeze it in, which in some cases that’s great. (Dont forget ft -auto)
- Running Get-Member (GM) on this we see that as it goes down the pipe its left in tact (Shows us we have an IPAddress object)
- The important part, if we pass an object down the pipe and then format it and then check the type, we see several items spit back, all of which are Internal.Format types.
The problem here is that what we’ve started with in the pipe (an IPAddress) has been dropped and changed for Format objects, which dont have the properties of the original object.
Basically we’ve gone from an Object to formatted text which is no fun in PowerShell.
There are some cases where you want to slim down your result set or combine things in which case the cmdlet you are looking for is Select-Object (Select) which I’ve now started to use over the format cmdlets for displaying data in the console.
$ip | select address $ip | select address | gm
You’ll notice the object type is only slightly different. the type name has been prefixed with Selected. The thing to keep in mind is if we try to use this IPAddress in a method that expects this object in its full state, for example.
$ip = [ipaddress] "..." #use a valid IP here $limitedIP = $ip | select addressfamily $ip | gm $limitedIP | gm [net.dns]::GetHostEntry [net.dns]::GetHostEntry($limitedIP) [net.dns]::GetHostEntry($ip)
You’ll notice the first call caused an error saying there is no overload that has a single argument which is rather misleading because both signatures require only one argument (IPAddress or String). It should probably say invalid type or something like that.
The second call works just fine.
Now, back to the format cmdlets. Normally I would have stopped right here but the PowerShell.com Tip of the Day had a pretty neat use of Format-Table in a function
$Input | Format-Table -AutoSize | Out-File $Path -Width 10000
This is actually a pretty neat use since it can be used to generate a report. I havent played with this enough to see if there is a great advantage of this method over the Export-CSV cmdlet, but still, it’s a use of the format commands in the pipe (never say never).
I hope this has helped clear things up for you more than making it worse. I will say I think one of the primary take aways from this post is that with all power that PowerShell provides its VERY easy to get yourself in trouble.
BONUS TIP: You can create custom objects quickly with Select like so.
$customobj = "" | select fname,lname $customobj.fname = 'Justin' $customobj.lname = 'Rich'
P.S. Thanks to Thomas Lee for the corrections.