Pr0no
Pr0no

Reputation: 4099

Count number of files in each subfolder, ignoring files with certain name

Consider the following directory tree

ROOT
    BAR001
        foo_1.txt
        foo_2.txt
        foo_ignore_this_1.txt
    BAR001_a
        foo_3.txt
        foo_4.txt
        foo_ignore_this_2.txt
        foo_ignore_this_3.txt
    BAR001_b
        foo_5.txt
        foo_ignore_this_4.txt
    BAR002
        baz_1.txt
        baz_ignore_this_1.txt
    BAR002_a
        baz_2.txt
        baz_ignore_this_2.txt
    BAR002_b
        baz_3.txt
        baz_4.txt
        baz_5.txt
        baz_ignore_this_3.txt
    BAR002_c
        baz_ignore_this_4.txt
    BAR003
        lor_1.txt

The structure will always be like this, so no deeper subfolders. I'm working on a script to count the number of files:

For the example above, this would result into:

Folder      Filecount
---------------------
BAR001      2
BAR001_a    2
BAR001_b    1

BAR002      1
BAR002_a    1
BAR002_b    3
BAR002_c    0

BAR003      1

I now have:

Function Filecount {
    param(
        [string]$dir
    )
    $childs = Get-ChildItem $dir | where {$_.Attributes -eq 'Directory'}

    Foreach ($childs in $child) {
        Write-Host (Get-ChildItem $dir | Measure-Object).Count;
    }
}


Filecount -dir "C:\ROOT"

(Not ready yet but building) This however, does not work. $child seems to be empty. Please tell me what I'm doing wrong.

Upvotes: 0

Views: 745

Answers (2)

FoxDeploy
FoxDeploy

Reputation: 13537

Well, to start, you're running ForEach ($childs in $child), this syntax is backwards, so that will cause you some issues! If you swap it, so that you're running:

ForEach ($child in $childs)

You'll get the following output:

>2
>2
>1
>1
>1
>3
>0

Alright, I'm back now with the completed answer. For one, instead of using Write-Out, I'm using a PowerShell custom object to let PowerShell do the hard work for me. I'm setting FolderName equal to the $child.BaseName, and then running a GCI on the $Child.FullName to get the file count. I've added an extra parameter called $ignoreme, that should have an asterisk value for the values you want to ignore.

Here's the complete answer now. Keep in mind that my file structure was a bit different than yours, so my file count is different at the bottom as well.

Function Filecount {
param(
    [string]$dir="C:\TEMP\Example",
    [string]$ignoreme = "*_*"
)
$childs = Get-ChildItem $dir | where {$_.Attributes -eq 'Directory'}

Foreach ($child in $childs) {
        [pscustomobject]@{FolderName=$child.Name;ItemCount=(Get-ChildItem $child.FullName | ? Name -notlike $ignoreme | Measure-Object).Count}

    }
}


>Filecount | ft -AutoSize

>FolderName ItemCount
>---------- ---------
>BAR001             2
>BAR001_A           1
>BAR001_b           2
>BAR001_C           0
>BAR002             0
>BAR003             0

If you're using PowerShell v 2.0, use this method instead.

Function Filecount {
param(
    [string]$dir="C:\TEMP\Example",
    [string]$ignoreme = "*_*"
)
$childs = Get-ChildItem $dir | where {$_.Attributes -eq 'Directory'}

Foreach ($child in $childs) {
    $ObjectProperties = @{
        FolderName=$child.Name
        ItemCount=(Get-ChildItem $child.FullName | ? Name -notlike $ignoreme | Measure-Object).Count}
    New-Object PSObject -Property $ObjectProperties
    }
}

Upvotes: 1

ConanW
ConanW

Reputation: 486

I like that way of creating an object 1RedOne, haven't seen that before, thanks.

We can improve the performance of the code in a few of ways. By using the Filter Left principle, which states that the provider for any cmdlet is inherently more efficient than running things through PowerShell, by performing fewer loops and by removing an unnecessary step:

    Function Filecount
{
    param
    (
        [string]$dir = ".",
        [parameter(mandatory=$true)]
        [string]$ignoreme
    )

    Get-ChildItem -Recurse -Directory -Path $dir | ForEach-Object `
    {
        [pscustomobject]@{FolderName=$_.Name;ItemCount=(Get-ChildItem -Recurse -Exclude "*$ignoreme*" -Path $_.FullName).count}
    }
}

So, firstly we can use the -Directory switch of Get-Childitem in the top-level directory (I know this is available in v3.0 and above, not sure about v2.0).

Then we can pipe the output of this directly in to the next loop, without storing it first.

Then we can replace another Where-Object with a provider -Exclude.

Finally, we can remove the Measure-Object as a simple count of the array will do:

Filecount "ROOT" "ignore_this" | ft -a

FolderName ItemCount
---------- ---------
BAR001             2
BAR001_a           2
BAR001_b           1
BAR002             1
BAR002_a           1
BAR002_b           3
BAR002_c           0
BAR003             1

Cheers Folks!

Upvotes: 1

Related Questions