nixda
nixda

Reputation: 2697

Exclude multiple subfolders while using Powershell's method Get-Childitem

What I have

The following working code lists all files from all subfolders where the script is placed.
It includes only certain file types and excludes all files from a certain single folder. So far so good

$fileInclude = @("*.txt", "*.hgr", "*.dat")           
$folderExclude = "C:\folder2"    
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

gci $scriptPath -recurse -include $fileFilter | Where {$_.FullName -notlike "$folderExclude*"} 

The file structure for my test was as shown

C:.
│   Myscript.ps1
│
├───folder1
│   │   file1.dat
│   │   file1.hgr
│   │   file1.txt
│   │
│   └───folder3
│           file3.dat
│           file3.hgr
│           file3.txt
│
└───folder2
        file3.dat
        file3.hgr
        file3.txt

What I want

Now I want to replace the second line

$folderExclude = "C:\folder2"

with an array. Later there are dozens of paths in it.

$folderExclude = @(
        "C:\folder2", 
        "C:\folder1\folder3"    
        )

Obviously this won't work since -notlike expects a string and not an array of strings.

I can't get my head around how to implement this. I think I need a for-each loop?

Upvotes: 5

Views: 7745

Answers (2)

mjolinor
mjolinor

Reputation: 68243

One way is to build a regex from $folderExclude that that matches any of the exclusion strings (using alternation), then use -notmatch instead of -notlike.

$folderExclude = @(
    "C:\folder2", 
    "C:\folder1\folder3"    
    )

[regex]$Exclude_regex = ‘(?i)^(‘ + (($folderExclude |foreach {[regex]::escape($_)}) –join “|”) + ‘)’

$fileInclude = @("*.txt", "*.hgr", "*.dat")           
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

gci $scriptPath -recurse -include $fileFilter | Where {$_.FullName -notmatch $Exclude_regex}

Edit: This might not be as "Posh" as the nested where-object, but it will be substantially faster.

Upvotes: 1

Keith Hill
Keith Hill

Reputation: 201592

You can also go this route:

$exclude = @('c:\folder2*', 'c:\folder1\folder3'} 
gci | Where {$fn = $_.Fullname; ($exclude | Where {$fn -like $_}).count -eq 0}

And if you are on V4, you can use the handy new common parameter -PipelineVariable:

gci -pv fse | Where {($exclude | Where {$fse.FullName -like $_}).count -eq 0}

Note that this approach has a flaw where if folder1 contains a file called folder3foobar, that file will get excluded. To fix that, you would need to modify the exclude terms to c:\folder1\folder3* and then when you compare against the fse.FullName, you would need to append a backslash if fse was a directory instead of a file.

Here's another approach that works based on -match allowing arrays on the left hand side:

 gci | Where {!($exclude -match [regex]::escape($_.Fullname))}

In this case, if one of the directories does match the $exclude array of terms, then there will be output and that is coerced to true but then gets inverted by the !.

Upvotes: 6

Related Questions