I found a PowerShell script that I thought I could adapt to my purposes.
It contains this line that formats the output:
$largeSizefiles = get-ChildItem -path $filesLocation -include $Extension -recurse -ErrorAction "SilentlyContinue" | ? { $_.GetType().Name -eq "FileInfo" } | where-Object {$_.Length -gt $fileSize} | sort-Object -property length | Select-Object Name, @{Name="Size In KB";Expression={ "{0:N0}" -f ($_.Length / 1KB)}},@{Name="LastWriteTime";Expression={$_.LastWriteTime}},@{Name="Path";Expression={$_.directory}} -first $filesLimit
The key part seems to be this:
Select-Object Name, @{Name="Size In KB";Expression={ "{0:N0}" -f ($_.Length / 1KB)}},@{Name="LastWriteTime";Expression={$_.LastWriteTime}},@{Name="Path";Expression={$_.directory}} -first $filesLimit
I have read the ss64.com tutorial on Select-Object, but I don't find anything to explain how the expressions of the form @{.....} are formatting the text.
The ss64.com page on the @ operator shows it in the format of @( ... ), with parens, not braces.
The code above results in the following output:
Name : RPI-Image-1-Copy.img
Size In MB : 29,477
Path : D:\VirtualDriveShare
LastWriteTime : 8/18/2015 6:27:51 PM
I'm familiar with a number of programming languages, but this is non-obvious to me, and I haven't found any clear explanation online. Can anyone point me to a good tutorial?
Select-Object can use a hash table of Header/Values for each item. In this example:
the script is selecting Name, then "Name in KB" that is derived from the current pipeline object's Length parameter.
This is further formatted by first dividing it by 1024 then using {0:N0} to display it.
Powershell uses the .Net string format syntax for display - in this case {0:N0} translates into:
You might want to take a look at Kathy Kam's Format 101 and Format 102 articles:
https://blogs.msdn.microsoft.com/kathykam/2006/03/29/net-format-string-101/
https://blogs.msdn.microsoft.com/kathykam/2006/09/29/net-format-string-102-datetime-format-string/
for further details on string formatting.
What you're missing is that
@{...}
indicates a hashtable array, which are composed of key-value pairs.As pointed out in the comments, there's a Technet article on this very thing. In your example, what's happening is the name/title is being assigned to the key in the hashtable, and a variable with a formatting expression is being assigned to the value in the hashtable.
Hashtables in PowerShell are fairly straightforward, but in case it's helpful, ss64's page is here, and Technet has a tutorial page as well.
I agree with all other answers, the hash-table being a calculated property in this case. I find these one liners wonderful, but they could be presented in a far better way. Technical still a one-liner but readability on a different scale.
If PoSh expects a continuation (after a
,
or a|
) you can simply insert a newline or of course after the line continuation char the backtic ` .@{...=...; ...=...}
is a hashtable. You may know of them as "dictionaries" in other languages. These are key/value pairs. Semicolons (;
) separate the different pairs, and the key and value are separated by an equal sign (=
). Note that the key can have implied quotes, making it a string.The
{ "{0:N0}" -f ($_.Length / 1KB) }
part is a script block. There are a bunch these; all theExpression
keys in the hashtables map to one. You can tell this by the curly braces without a prefix surrounding it. These are essentially anonymous functions (lambdas). The$_
is the current item from the pipeline, so when this script block is executed, it expands to a single item from the pipeline input.The particular script block
"{0:N0}" -f ($_.Length / 1KB)
part is just using the-f
operator to do formatting. This is equivalent to .NET'sString.Format
, where the pattern string is on the left and the arguments for the pattern are an array of objects on the right. The0
is just the argument index (so first argument) and the:N0
tells it to format as a number with 0 decimal places.Now between all the hashtables, we have a bunch of comma. These make an array! It turns out that the surrounding
@(...)
part is optional.So we have an array containing a few strings and a bunch of hashtables with the keys
Name
andExpression
, and that array is an argument being passed intoSelect-Object
. Namely, it's the-Property
argument. Note this line from SS64's doc:So this all makes a little more sense now. It's just
Select-Object
transforming the input, and it's putting together a bunch of "calculated" (determined by script block) attributes and one "uncalculated" (already present on the incoming object) attribute for the output object.A minor note: I believe this particular calculated attribute is somewhat silly:
I don't see any reason it couldn't have just been selected directly like
Name
was.