Andrew Savinykh
Andrew Savinykh

Reputation: 26349

Can you filter file search based on folder names?

I'm pretty sure I got it right, but want to double check with community. I have a folder structure that starts, say from c:\test\ it has many files and folders. I also have a folder in this structure, say c:\test\something\another\temp\ this folder and its subfolders account for about 50% or more of all the files in c:\test\.

I want to get a list of all *.txt files in c:\test\ and its subfolders, excluding those, that are in c:\test\something\another\temp\ and deeper.

I don not want to enumerate the whole thing including c:\test\something\another\temp\ and then pipe out and filter for performance reason: there is just no need to walk through the whole c:\test\something\another\temp\ tree, since there is nothing of interest there.

It does not look like Get-ChildItem cmdlet can help me here, I have to resort to .NET calls and write the recursion myself to exclude the folder I don't need.

Get-ChildItem has -Exclude parameter but it does not work on folders, only on files.

I'm fine with calling .NET library I'll just want to make sure I'm not missing anything and there is no <easier> way to do this with stock powershell cmdlets.

Is there?

Upvotes: 0

Views: 893

Answers (4)

mjolinor
mjolinor

Reputation: 68341

If the directory is really large,

(cmd /c dir c:\test\*.txt /b /s) -notlike 'c:\test\something\another\temp\*'

should give you much quicker results than get-childitem.

Edit:

Here a different version that doesn't search the excluded directories:

$files = 
(cmd /c dir c:\test /b /s /ad) -notlike 'c:\test\something\another\temp*' |
 foreach { iex "cmd /c dir $_\*.txt /b /s /a-d"  }

Don't know how to get it any quicker than that.

Upvotes: 3

Eris
Eris

Reputation: 7638

Another option utilizing the pipeline:

function Get-FilesInDir {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true)]
        $Path,
        $Exclude
    )

    Process {
        $Path | ForEach-Object {
            if( (Resolve-path $_).Path.StartsWith($Exclude) ) { return }

            Get-ChildItem -Path:$_ |
                ForEach-Object { 
                    if($_.PSIsContainer) { Get-FilesInDir -Path:$_.FullName -Exclude:$Exclude }
                    $_.FullName
                }
        }
    }
}

Upvotes: 0

Keith Hill
Keith Hill

Reputation: 202072

Or you could do:

Get-ChildItem C:\test -r *.txt | Where {$_.FullName -notlike 'c:\test\something\another\temp\*'}

Upvotes: 1

alroc
alroc

Reputation: 28194

You can do this by creating your own function which recursively calls itself, skipping the directory you don't want to look at. I think it's doing what you're envisioning in your mind, but just with built-in cmdlets instead of resorting to .NET Framework methods.

$searchroot = 'c:\test';
function Get-FilesInDir {
param (
    $DirToSearch
)
    $ItemsInDir = Get-ChildItem -path $DirToSearch;
    foreach ($item in $ItemsInDir) {
        if ($item.FullName -eq 'c:\test\something\another\temp') {
            continue;
        }
        if ($item.PSIsContainer) {
            Get-FilesInDir $item.FullName;
        }
        $item.FullName;
    }
}

Get-FilesInDir $searchroot

This will output the full path to each file found.

If you have a list of directories you want to exclude, you can put them into an array and modify the if test around the continue to check each path to see if it's in that array.

Upvotes: 0

Related Questions