简体   繁体   中英

Recursively count files in subfolders

I am trying to count the files in all subfolders in a directory and display them in a list.

For instance the following dirtree:

TEST
    /VOL01
        file.txt
        file.pic
    /VOL02
        /VOL0201
            file.nu
            /VOL020101
                file.jpg
                file.erp
                file.gif
    /VOL03
        /VOL0301
            file.org

Should give as output:

PS> DirX C:\TEST

Directory              Count
----------------------------
VOL01                      2
VOL02                      0
VOL02/VOL0201              1
VOL02/VOL0201/VOL020101    3
VOL03                      0
VOL03/VOL0301              1

I started with the following:

Function DirX($directory)
{
    foreach ($file in Get-ChildItem $directory -Recurse)
    {
        Write-Host $file
    }
}

Now I have a question: why is my Function not recursing?

Something like this should work:

dir -recurse |  ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }
  • dir -recurse lists all files under current directory and pipes (|) the result to
  • ?{ $_.PSIsContainer } which filters directories only then pipes again the resulting list to
  • %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count } which is a foreach loop that, for each member of the list ($_) displays the full name and the result of the following expression
  • (dir $_.FullName | Measure-Object).Count which provides a list of files under the $_.FullName path and counts members through Measure-Object

  • ?{ ... } is an alias for Where-Object

  • %{ ... } is an alias for foreach

Similar to David's solution this will work in Powershell v3.0 and does not uses aliases in case someone is not familiar with them

Get-ChildItem -Directory | ForEach-Object { Write-Host $_.FullName $(Get-ChildItem $_ | Measure-Object).Count}

Answer Supplement

Based on a comment about keeping with your function and loop structure i provide the following. Note : I do not condone this solution as it is ugly and the built in cmdlets handle this very well . However I like to help so here is an update of your script.

Function DirX($directory)
{
    $output = @{}

    foreach ($singleDirectory in (Get-ChildItem $directory -Recurse -Directory))
    {
        $count = 0 
        foreach($singleFile in Get-ChildItem $singleDirectory.FullName)
        {
            $count++
        }
        $output.Add($singleDirectory.FullName,$count)
    }

    $output | Out-String
}

For each $singleDirectory count all files using $count ( which gets reset before the next sub loop ) and output each finding to a hash table. At the end output the hashtable as a string. In your question you looked like you wanted an object output instead of straight text.

Well, the way you are doing it the entire Get-ChildItem cmdlet needs to complete before the foreach loop can begin iterating. Are you sure you're waiting long enough? If you run that against very large directories (like C:) it is going to take a pretty long time.

Edit: saw you asked earlier for a way to make your function do what you are asking, here you go.

Function DirX($directory)
{
    foreach ($file in Get-ChildItem $directory -Recurse -Directory )
    {
        [pscustomobject] @{
        'Directory' = $File.FullName
        'Count' = (GCI $File.FullName -Recurse).Count
        }
    }
}
DirX D:\

The foreach loop only get's directories since that is all we care about, then inside of the loop a custom object is created for each iteration with the full path of the folder and the count of the items inside of the folder.

Also, please note that this will only work in PowerShell 3.0 or newer, since the -directory parameter did not exist in 2.0

My version - slightly cleaner and dumps content to a file

Original - Recursively count files in subfolders

Second Component - Count items in a folder with PowerShell

$FOLDER_ROOT = "F:\"
$OUTPUT_LOCATION = "F:DLS\OUT.txt"
Function DirX($directory)
{
    Remove-Item $OUTPUT_LOCATION

    foreach ($singleDirectory in (Get-ChildItem $directory -Recurse -Directory))
    {
        $count = Get-ChildItem $singleDirectory.FullName -File | Measure-Object | %{$_.Count}
        $summary = $singleDirectory.FullName+"    "+$count+"    "+$singleDirectory.LastAccessTime
        Add-Content $OUTPUT_LOCATION $summary
    }
}
DirX($FOLDER_ROOT)

I modified David Brabant's solution just a bit so I could evaluate the result:

$FileCounter=gci "$BaseDir" -recurse |  ?{ $_.PSIsContainer } | %{ (gci "$($_.FullName)" | Measure-Object).Count }
Write-Host "File Count=$FileCounter"
If($FileCounter -gt 0) {
  ... take some action...
}
Get-ChildItem $rootFolder `
    -Recurse -Directory | 
        Select-Object `
            FullName, `
            @{Name="FileCount";Expression={(Get-ChildItem $_ -File | 
        Measure-Object).Count }} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM