Powershell was born with ease of use in mind, it has a fairly flexible syntax and is good at guessing the user’s intention. For example the Addition operator can deal with math if the passed values are numbers:
PS >_ $a = 1
PS >_ $b = 2
PS >_ $a + $b
3
It can also properly concatenate strings if the passed values are of that type:
PS >_ $a = 'sample_'
PS >_ $b = 'string'
PS >_ $a + $b
sample_string
When used interactively at the console, Powershell tries to print a nice textual data representation with tables, lists and so on.
For the sake of this discussion let’s assume we want to filter a list of folders:
PS >_ Get-ChildItem -Directory
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 4/8/2019 1:23 PM Microsoft.ADHybridHealthService
d----- 4/8/2019 1:23 PM Microsoft.Advisor
d----- 4/8/2019 1:23 PM Microsoft.AlertsManagement
d----- 4/8/2019 1:23 PM Microsoft.Authorization
d----- 4/8/2019 1:23 PM Microsoft.Automation
d----- 4/8/2019 1:23 PM Microsoft.Billing
d----- 4/8/2019 1:23 PM Microsoft.Cache
d----- 4/8/2019 1:23 PM Microsoft.ClassicCompute
d----- 4/8/2019 1:23 PM Microsoft.ClassicNetwork
d----- 4/8/2019 1:23 PM Microsoft.ClassicStorage
d----- 4/8/2019 1:23 PM Microsoft.ClassicSubscription
d----- 4/8/2019 1:23 PM Microsoft.Commerce
d----- 4/8/2019 1:23 PM Microsoft.Compute
d----- 4/8/2019 1:23 PM Microsoft.Consumption
d----- 4/8/2019 1:23 PM Microsoft.CostManagement
d----- 4/8/2019 1:23 PM Microsoft.DocumentDB
We want to find the ones whose name matches “aut”:
PS >_ Get-ChildItem -Directory | Where-Object 'Name' -match 'aut'
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 4/8/2019 1:23 PM Microsoft.Authorization
d----- 4/8/2019 1:23 PM Microsoft.Automation
If we want to know how many folder match the filter we have a number of options: pipe the output to Measure-Object or use the Count or Length properties of the returned list:
PS >_ (Get-ChildItem -Directory | Where-Object Name -match 'aut').Count
2
PS >_ (Get-ChildItem -Directory | Where-Object Name -match 'aut').Length
2
Now let’s say you only want the folder names that contain “Automation”, it is a simple filter change:
PS >_ (Get-ChildItem -Directory | Where-Object Name -match 'automation' | Select-Object -ExpandProperty 'Name').Count
1
PS >_ (Get-ChildItem -Directory | Where-Object Name -match 'automation' | Select-Object -ExpandProperty 'Name').Length
20
Wait, what? Why the difference? ?
Let’s test with files this time.
PS >_ Get-ChildItem
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/8/2019 10:59 AM 0 Microsoft.ADHybridHealthService.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Advisor.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.AlertsManagement.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Authorization.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Automation.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Billing.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Cache.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.ClassicCompute.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.ClassicNetwork.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.ClassicStorage.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.ClassicSubscription.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Commerce.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Compute.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Consumption.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.CostManagement.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.DocumentDB.txt
Again, let’s filter for the files whose name contains the string “aut”
PS >_ Get-ChildItem -File | Where-Object 'Name' -match 'aut'
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/8/2019 10:59 AM 0 Microsoft.Authorization.txt
-a---- 4/8/2019 10:59 AM 0 Microsoft.Automation.txt
PS >_ (Get-ChildItem -File | Where-Object 'Name' -match 'aut').Length
2
We get two files as expected.
Now I only want files matching “Automation”:
PS >_ (Get-ChildItem -File | Where-Object 'Name' -match 'Automation').Length
0
Still no what I would expect. What’s happening? ?
Well, it turns out Powershell is trying to be helpful. Seriously. It is trying to be helpful and guess our intention (and failing, you might think ?). Anyway if we analyze the behavior carefully, it makes sense.
Let’s start with the folder example:
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -match 'automation').Count
1
Here we are listing all folders whose name matches the word “Automation” and finally we are counting how many names we have (only one match, correct).
Now this:
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -match 'automation' | Select-Object -ExpandProperty 'Name').Length
20
Here we are again listing all folders whose name matches the word “Automation”, we are extracting and expandingthe Name property and finally we are asking for its Length. Here’s what happens if we simplify the filter and just use the resulting name to print its Count and Length properties:
PS >_ 'Microsoft.Automation'.Count
1
PS >_ 'Microsoft.Automation'.Length
20
‘Microsoft.Automation’ is one string so Count returns 1, white ‘Microsoft.Automation’ is twenty characters long, so Length is returning 20.
Why listing the file returns zero then? Again, that’s Powershell trying to be helpful: if you have a file object, the Length property refers to the file size (its length in bytes). In fact, if you check carefully my previous output you’ll see the file is empty:
PS >_ (Get-ChildItem -File | Where-Object 'Name' -match 'Automation')
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/8/2019 3:03 PM 0 Microsoft.Automation.txt
Just to double-check, let’s append some text to the file:
PS >_ Out-File -Path .\Microsoft.Automation.txt -Append -InputObject (Get-Date)
PS >_ (Get-ChildItem -File | Where-Object 'Name' -match 'Automation')
Directory: C:\varCount
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/8/2019 3:15 PM 38 Microsoft.Automation.txt
PS >_ (Get-ChildItem -File | Where-Object 'Name' -match 'Automation').Length
38
That’s it. Since Powershell is based on .NET and everything in .NET is an object (therefore has a Type), everything in Powershell has a Type even if we don’t explicitly specify it. The Type we are working with influences how Powershell treats the data and what returns to us. Let’s take a look at the Type returned by each command we worked with:
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -Match 'aut').Length
2
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -Match 'aut').GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Since we have two folders matching the filter “aut” we get back an Array, and the Lengh property indicates the Length of the array: 2.
Next:
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -Match 'automation' | Select-Object -ExpandProperty 'Name').GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS >_ (Get-ChildItem -Directory | Where-Object 'Name' -Match 'automation' | Select-Object -ExpandProperty 'Name').Length
20
Now we are getting a String (the folder Name), and the Length of a string is, well, it’s length… 20 characters in this case.
As mentioned, the Length property for a file (System.IO.FileSystemInfo type) is its length in bytes, in other words the file’s size
PS >_ (Get-ChildItem -File | Where-Object 'Name' -Match 'automation').GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False FileInfo System.IO.FileSystemInfo
PS >_ (Get-ChildItem -File | Where-Object 'Name' -Match 'automation').Length
38
So, what if we really want to count the number of files (or folders, or other objects) rather than whatever value Length or Count return for that type? In other words, how can we treat all output as an array (and have Powershell not try so hard to be helpful? ?)
The answer is to use the array notation:
PS >_ @(Get-ChildItem -File | Where-Object 'Name' -Match 'automation').GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS >_ @(Get-ChildItem -File | Where-Object 'Name' -Match 'automation').Length
1
Life is hard. After all it kills you. – Katharine Hepburn
P.s.
As a non native English speaker, while writing this post I had doubts about my use of “whose” referred to “things” (Powershell objects are certainly neither people nor animals ☺️); I like this explanation from Merriam-Webster so blame it on Shakespeare if you don’t like it ?