powershell Get-ChildItem result in array
The one value you get is indeed the length of the resulting array - the Array class has an explicit Length
property that takes precedence over property enumeration.
To get the individual Length
property values from each item in the array, pipe to either Select-Object
or ForEach-Object
, like so:
Get-ChildItem -File -Recurse -Path $path |ForEach-Object -MemberName Length# or Get-ChildItem -File -Recurse -Path $path |Select-Object -ExpandProperty Length
To complement Mathias R. Jessen's helpful answer:
tl;dr
Use of the .ForEach()
array method enables a concise and performant solution:
# Return the files' .Length property values as a collection.(Get-ChildItem -File -Recurse -Path $path).ForEach('Length')
As pointed out in Mathias' answer, type-native properties - such as .Length
on array instances - take precedence over the so-called member enumeration that you tried to perform - that is, you wanted to collect the .Length
property values of the elements of your collection (array; the System.IO.FileInfo
instances output by Get-ChildItem
), and return them as an ([object[]]
) array.
- GitHub issue #7445 discusses this situational ambiguity and proposes introducing a dedicated operator, say
%.
so that you can unambiguously request either regular property access or member enumeration.
A perhaps surprising aspect of member enumeration is that it applies pipeline logic in that it situationally returns a scalar, namely if the collection happens to contain just one element.
- E.g.,
@(Get-Date).Year.GetType().Name
returns anSystem.Int32
, notObject[]
, indicating that an integer rather than a (single-element) array containing an integer was returned. - GitHub issue #6802 discusses this.
Member enumeration is not only convenient, but also fast, because it avoids looping / enumeration in PowerShell code.
Use of the ForEach-Object
or Select-Object
cmdlets, as shown in Mathias' answer, is definitely a functional but slow workaround for not being able to use member enumeration in a given situation, due to use of the pipeline, which isn't necessary for input that that is already in memory in full.
Therefore, use of the .ForEach()
array method - using the overload where you simply specify the target property name ('Length'
) - is a concise and better-performing alternative.
- Note: Unlike with member enumeration, the return value from
.ForEach()
is always a collection, albeit not an array: it is of type[System.Collections.ObjectModel.Collection[psobject]]
, which, however, will behave like an array in most contexts.[1]
[1] Unlike an array, this type of collection is extensible (you can add or remove elements). Due to the element type being [psobject]
, each element is typically invisibly wrapped in this type; e.g., 42 -is [psobject]
is $false
, but @(42).ForEach({ $_ })[0] -is [psobject]
is $true
. Unfortunately, there are cases where this near-invisible wrapper results in different behavior - see GitHub issue #5579.
In your example you get the Length
of the resulting array.
Get-ChildItem -File
returns an array of System.IO.FileInfo
. So you need to take (select) the Length
attribute of every entry in the array. This is done by using Select-Object
or just select
:
Get-ChildItem -File -Recurse -Path $path | Select-Object Length
which would give you an array of objects containing the file's length as Length
property. You can also select multiple properties:
Get-ChildItem -File -Recurse -Path $path | Select-Object Name, Length