Search code examples
powershellreportingpowershell-4.0

How to format PowerShell results from ForEach


I am trying to get complete a request of amount of files in a directory and then the total size of that directory. I've come up with this:

Get-Content -Path C:\Users\$USERNAME%\Documents\list.txt |
    Foreach-Object {
        cd $_
        Write-Host $_ 
        (Get-ChildItem -Recurse -File | Measure-Object).Count 
        (ls -r|measure -sum Length).Sum
    }

The txt file has contents such as:

\\directory\on\network\1
\\directory\on\network\also

Ultimately I need this in a spreadsheet, but I am failing with formatting. As is, it outputs straight to powershell, so with thousands of directories this isn't ideal. I've tried exporting to CSV but it overwrites the CSV with each result, and when I tried setting the function equal to a variable array and then exporting that, it simply output a blank file.

Any assistance with this is appreciated.


Solution

  • In order to export to CSV you will need an object with properties. Your code generates a few values without any structure. Surely the % in your sample code is a typo, it definitely doesn't belong there. It is generally considered bad practice to use aliases in scripts, however you should, at a minimum, keep it consistent. One line you use Get-ChildItem/Measure-Object and the next use ls/measure. Regardless you don't show your export, so it's hard to help with what we can't see. You also don't need to CD into the directory, it seems it would only slow the script down if anything.

    The easiest way I know to create an object is to use the [PSCustomObject] type accelerator.

    $infile = "C:\Users\$USERNAME\Documents\list.txt"
    $outfile = "C:\some\path\to.csv"
    
    Get-Content -Path $infile |
        Foreach-Object {
            Write-Host Processing $_
            [PSCustomObject]@{
                Path  = $_
                Total = (Get-ChildItem $_ -Recurse -File | Measure-Object).Count 
                Size  = (Get-ChildItem $_ -Recurse -File | Measure-Object -sum Length).Sum
            }
        } | Export-Csv $outfile -NoTypeInformation
    

    Edit

    We should've ran the Get-Childitem call once and then pulled the info out. The first option is in "pipeline" mode can save on memory usage but might be slower. The second puts it all in memory first so it can be much quicker if it's not too large.

    Get-Content -Path $infile |
        Foreach-Object {
            Write-Host Processing $_
            $files = Get-ChildItem $_ -Recurse -File | Measure-Object -sum Length
            [PSCustomObject]@{
                Path  = $_
                Total = $files.Count 
                Size  = $files.Sum
            }
        } | Export-Csv $outfile -NoTypeInformation
    

    or

    $results = foreach($folder in Get-Content -Path $infile)
    {
        Write-Host Processing $folder
        $files = Get-ChildItem $folder -Recurse -File | Measure-Object -sum Length
        [PSCustomObject]@{
            Path  = $folder
            Total = $files.Count 
            Size  = $files.Sum
        }
    }
        
    $results | Export-Csv $outfile -NoTypeInformation