I'd like to query a directory with Get-ChildItem and create a table with columns like Path, Size(in Gb), MinimumCreationTime, MaximumCreationTime. In foreach cycle I wrote 3 Measure commands. Is it possible Measure multiple properties with one command?
$pathes = @'
C:\open
C:\games
'@.Split([System.Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
foreach ($path in $pathes){
Get-ChildItem $path -Recurse | Measure Length -Sum
Get-ChildItem $path -Recurse | Measure CreationTime -Minimum
Get-ChildItem $path -Recurse | Measure CreationTime -Maximum
}
It is possible with a single call to the Measure-Object
command, using a calculated property that converts the CreationTime
property to numeric type. Now -Sum
can work with that (albeit we'll discard the sum for CreationTime
).
After we've calculated the stats, we convert back to [DateTime]
to get meaningful display values.
Since PS 7+, a calculated property can be used as Measure-Object
argument. For older PS versions, we can use Select-Object
to create a calculated property.
foreach ($path in $pathes){
$stats = Get-ChildItem $path -File -Recurse |
Measure-Object 'Length', { $_.CreationTime.Ticks } -Sum -Minimum -Maximum
# Create the output for one table row
[PSCustomObject]@{
Path = $path
'Size(GB)' = [math]::Round( $stats[0].Sum / 1GB, 2 ) # 2 = number of digits
MinimumCreationTime = [DateTime] [Int64] $stats[1].Minimum
MaximumCreationTime = [DateTime] [Int64] $stats[1].Maximum
}
}
Explanation:
Measure-Object
:
Length
CreationTime
to Int64
and uses it as value that will be measured.Measure-Object
, it outputs an array that contains an object for each property, which contains the stats.
$stats[0]
contains the Sum
, Minimum
and Maximum
for the Length
property, of which we only take the Sum
.$stats[1]
contains the Sum
, Minimum
and Maximum
for the CreationTime
property, of which we only take the Minimum
and Maximum
. Note that Measure-Object
produces output of type [double]
, so we first have to convert back to [Int64]
before finally converting back to [DateTime]
.foreach ($path in $pathes){
$stats = Get-ChildItem $path -File -Force |
Select-Object Length, @{ name = 'CreationTimeTicks'; expression = { $_.CreationTime.Ticks } } |
Measure-Object Length, CreationTimeTicks -Sum -Minimum -Maximum
# Create the output for one table row - identical to PS 7+ solution
[PSCustomObject]@{
Path = $path
'Size(GB)' = [math]::Round( $stats[0].Sum / 1GB, 2 ) # 2 = number of digits
MinimumCreationTime = [DateTime] [Int64] $stats[1].Minimum
MaximumCreationTime = [DateTime] [Int64] $stats[1].Maximum
}
}
Explanation:
This is similar to the PS 7+ solution, except that we use Select-Object
to create a calculated property named CreationTimeTicks
, so we can pass it by name to the Measure-Object
call.
While this code appears to work, the code presented by this answer is conceptually much clearer, so I would go with it.