Measure-object use in advanced function; no totals

(Ref# 40398)
I have verified that measure-object works on a collection of objects obtained from get-childitem. Here is the code"

 $var1=Get-ChildItem -path C:\Users\Peter\Documents\TestArchive -Recurse
$var1 | Measure-Object -InputObject {$_.length} -sum | Select-Object @{name='Total File Size in GB';expression={$_.sum/1e9}}, @{name='File Count';expression={$_.count}}

Here is the output:

                                                                 Total File Size in GB                                                                                 File Count
                                                                 ---------------------                                                                                 ----------
                                                                           0.039386879                                                                                         37

Here is the function code I am trying to build:

    Process{
    $total=Get-ChildItem -Path C:\Users\Peter\Documents\TestArchive -Recurse 
        foreach($file in $total){
        if((Get-ItemProperty -Path $file.FullName).Attributes -band [io.fileattributes]::archive){
        Measure-Object -inputobject ($file).length -sum | Write-Output

The if statement returns true for files that have the archive bit set; then the measure-object cmdlet works on each file one at a time, instead of the entire collection of files as in the example above. So I get this output:

Count    : 1
Average  : 
Sum      : 1100072
Maximum  : 
Minimum  : 
Property : 

Count    : 1
Average  : 
Sum      : 1148510
Maximum  : 
Minimum  : 
Property : 

etc… I’ve tried using hash tables, but no luck. Any ideas out there?
Peter

So it’s pretty obvious when each measure object has a count of ‘1’ that the foreach loop is breaking the files down and measuring them individually. If you want to do it that way, you have to accumulate their length values into a variable like this:

$Files = Get-ChildItem -Path 'C:\Users\Peter\Documents\TestArchive' -Recurse
foreach ($File in $Files){
    if((Get-ItemProperty -Path $file.FullName).Attributes -band [io.fileattributes]::archive){
        $TotalSize = $TotalSize + $File.Length
    }
}
$SizeByGB = $TotalSize / 1GB
"I have {0} files with a total size of {1:N2}" -f $Files.Count, $SizeByGB

Still this line worries me. Casting, enumeration, binary and, OH MY???

if((Get-ItemProperty -Path $file.FullName).Attributes -band [io.fileattributes]::archive)

Don’t get too ahead of yourself, take time to learn the basics it’ll help you deconstruct lines like this and sometimes you may find there’s an easier way to accomplish the same task.

$Files = Get-ChildItem 'C:\Users\Peter\Documents\TestArchive' -Attributes Archive -Recurse
$TotalFiles = $Files | Measure-Object -Property Length -Sum
$SizeByGB = $TotalFiles.Sum / 1GB
"I have {0} files with a total size of {1:N2}" -f $TotalFiles.Count, $SizeByGB

Spotted a flaw in my first code block after posting but I’ll let you find it. ; )

Jack: Was the flaw not initializing the variable $Totalsize? I did so in my begin block, along with another variable $Totalnumber. The line you mention was lifted directly from Ed Wilson scripting blog. I see how it works, and it seems very succinct: for each file found, the get-property cmdlet examines it to see if there is an archive bit set. all other attributes are ignored. If the file is archive, then the boolean resolves to 1 = true, and the next two lines of code are executed. This was so simple. I’m just not thinking like a programmer…been many years since Fortran Buff 40. Here is the new function, which works fine:

function Get-ArchiveSize
{
    [CmdletBinding()]
    Param
    (
        # Path to the head archive directory is required
        [Parameter(Mandatory=$true,
                    ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
                  [string] $Path
    )

    Begin
    {$totalsize=0
     $totalnumber=0
    }
    Process{
    $total=Get-ChildItem -Path $Path -Recurse 
        foreach($file in $total){
        if((Get-ItemProperty -Path $file.FullName).Attributes -band [io.fileattributes]::archive){
        $totalsize=$totalsize + $file.length
        $totalnumber=$totalnumber + 1}
}
       $hash=[ordered]@{'Total Size of Files in GB'=$totalsize/1e6;
                 'Total Number of Files'=$totalnumber}
                   $Obj=New-Object -TypeName PSObject -Property $hash
                           Write-Output $Obj}
 }

BTW, I also was using lines #1 and 2 in your example, but I believe they are incorrect code. Child-item has two parameter sets, and -path, -recurse are in the first set, and -attributes is only in the second set. I was getting inconsistent results and finally remembered something from the first Powershell class where Jason was explaining parameter syntax. Also, the -property parameter for measure-object does not accept pipeline input: you must use -inputobject. So instead, I began using this code:

 
$var1=Get-ChildItem -path C:\Users\Peter\Documents\TestArchive -Recurse
$var1 | Measure-Object -inputobject {$_.length} -Sum

However, I was now back to selecting ALL files. I also tried using where-object -property attributes, but this was also a non-pipeline parameter. Again, it seemed to work, but files that were both archive and readonly were not picked up. So Ed Wilson seemed to have the only solution for identifying a file with any attribute (maybe).
I haven’t seen the -f feature in your last line. What is that?

Filtering by -Attributes and by Where-Object give equal results

PS D:\> Get-ChildItem -Path d:\! -Recurse  | Where-Object { $_.Attributes -band [io.fileattributes]::readonly } | Measure-Object -Sum -Property Length

Count    : 2
Average  :
Sum      : 7059789312
Maximum  :
Minimum  :
Property : Length

PS D:\> Get-ChildItem -Path d:\! -Recurse -Attributes Readonly | Measure-Object -Sum -Property Length


Count    : 2
Average  :
Sum      : 7059789312
Maximum  :
Minimum  :
Property : Length

-f - is formatting operator

Get-Help About_Operators

http://ss64.com/ps/syntax-f-operator.html

#btw you can free use any combinations :)
# find readonly inside 'archive'
PS D:\> Get-ChildItem -Path d:\! -Recurse -Attributes Archive | Where-Object { ($_.Attributes -band 'readonly')  } | Measure-Object -Sum -Property Length


Count    : 3
Average  :
Sum      : 7059790161
Maximum  :
Minimum  :
Property : Length

# the same thing
PS D:\> Get-ChildItem -Path d:\! -Recurse | Where-Object { ($_.Attributes -band 'readonly') -and ($_.Attributes -band 'Archive')  } | Measure-Object -Sum -Property Length


Count    : 3
Average  :
Sum      : 7059790161
Maximum  :
Minimum  :
Property : Length

# readonly with no archive bit
PS D:\> Get-ChildItem -Path d:\! -Recurse | Where-Object { ($_.Attributes -band 'readonly') -and (-not ($_.Attributes -band 'Archive'))  } | Measure-Object -Sum -Property Length


Count    : 1
Average  :
Sum      : 849
Maximum  :
Minimum  :
Property : Length
Jack: Was the flaw not initializing the variable $Totalsize?
Nope, PS is pretty good about not having the declare simple variable types like integers but it is good practice to declare them anyway b/c you could unintentionally keep adding to that variable each time you run the script giving confusing results. My flaw was $Files.Count represented the total number of files, not just the ones with the archive attribute on.
BTW, I also was using lines #1 and 2 in your example, but I believe they are incorrect code. Child-item has two parameter sets, and -path, -recurse are in the first set, and -attributes is only in the second set.
Are you sure? Max did a good job testing this out.
Also, the -property parameter for measure-object does not accept pipeline input: you must use -inputobject.
You're right in that the -Property param does not take pipeline input but if you look again I didn't pipe that value to Measure-Object. Furthermore, I think you're mixing up the -Property and -InputObject params. Looks like you're using -InputObject to specify what property you want to be measured but that is in fact the purpose of the -Property param. -InputObject is used to measure a single object and was designed to be used "instead of piping".
I haven't seen the -f feature in your last line. What is that?
I learned that little trick from someone on this forum a few years ago. I'd love link to his posts to give him credit but I can't seem to search my old posts and the site is crawling for me this morning. But yea, it's handy to insert variables into an output string without making it really messy.
I learned that little trick from someone on this forum a few years ago. I'd love link to his posts to give him credit but I can't seem to search my old posts and the site is crawling for me this morning.
And not long after I post this, he posts on another thread, haha... life is cool. Rob Simmers

Max, thanks for showing me the correct syntax for combined boolean expressions used with where-object. I must have tried different combinations for an hour or so. I have saved these examples in my script folder.

Jack, I was certainly confused about how cmdlets like measure-object and select-object work in the pipeline. I went back and read chapters on the pipeline in “Learn Windows Powershell…” and I see what is going on. The -property parameter does not accept pipeline input, but it doesn’t have to; the connection is automatic since this cmdlet accepts ALL types of input. I saw an explanation of the difference in using input parameter where the entire collection of objects gets passed through as one array. Thanks again

Now onto finishing “Learn Powershell Toolmaking in a Month of Lunches” so I can build a module…
Peter