Where-Object search for Part of Result in Array

Hi, i have a tricky issue…

i try to get-childitems of Typ Directory and want to exclude dedicated Directorys from the result:

$FolderstoSkip = @(“c:\Windows”,“C:\Temp”)
$Folderlist += (Get-Childitem $Drive.Root -Recurse -Directory | where { — Folder is not part of the exluded Folders or an Subfolder of it…— })

But i struggle a bit with the phrase i should use here… i dont want to run through in a for-next loop… any idea will kindly appreciated…

Example:
Element in Get-Childitem is “C:\Windows\System32” what is a subfolder of “c:\windows” and have to been excluded…

Thank you in Advenced
Martin

Martin,
Welcome to the forum. :wave:t4:

That’s a little tricky indeed …

I used to use a regex match pattern in such cases. But since the backslash is a special character in regex you have to prepare the pattern in advance.

$ExcludeList = 'c:\windows', 'C:\temp'
$ExcludePattern = 
@(
    foreach ($element in $ExcludeList) {
        [regex]::Escape($element)
    }
) -join '|'

$Folderlist = 
    Get-Childitem -Path C:\ -Directory -Recurse | 
        Where-Object -Property FullName -NotMatch -Value $ExcludePattern 

$Folderlist
2 Likes

This will be inefficient due to it checking every folder for a match even those folders you want to exclude. I think a simpler approach would be to get the root directory list first and then feed those to a recursive search. This will eliminate the processing of all child directories within the excluded directories.

$excludelist = 'c:\windows', 'C:\temp'

$folderlist = Get-ChildItem -Path c:\ -Directory -ErrorAction SilentlyContinue -ErrorVariable errors |
    Where-Object Name -NotIn $excludelist | Get-ChildItem -Recurse -Directory -ErrorAction SilentlyContinue -ErrorVariable +errors

I also added -ErrorAction and -ErrorVariable so the console remains clean as there are certainly going to be access denied to a few directories. You can inspect the variable $errors to see which directories resulted in error.

You can also clean the code up/make it more readable by using splatting.

$excludelist = 'c:\windows', 'C:\temp'

$gciparams = @{
    Directory     = $true
    ErrorAction   = 'SilentlyContinue'
    ErrorVariable = '+errors'
}

$folderlist = Get-ChildItem -Path 'C:\' @gciparams |
    Where-Object Name -NotIn $excludelist |
        Get-ChildItem -Recurse @gciparams
2 Likes

Good point. But if the folders to exclude do not belong to the top level it gets even trickier. :wink:

This is also a good point. For this example it’s true. As Olaf points out, if you needed to start excluding folders that are deeper, his suggestion may be the best choice.

So as almost always … it depends … :stuck_out_tongue_winking_eye: :+1:t4: :love_you_gesture:t4: :wink: :slightly_smiling_face:

1 Like

Amazing… you guys are awesome. thank you very much… Seems i have much to learn. And also thank you for the warm welcome…

In Fact, the Problem is, there are several subfolders to exclude so the inefficient way seems to be the best. i will run my script the next days agains the SAN machines and see how fast it will be.

again, thank you very much!
happy Halloween

If possible … it’s always way faster to run a script locally than to run it against a UNC path or a mapped share. :point_up_2:t4:

I have tested both and this is the Outcome:

Local Drive C only, Windows Folder excluded:
Olaf Methode 1: 18 seconds
krzydoug Methode 2: 8 seconds

SAN with about 500.000 Folders, mapped as Drive with exlusion of a rootfolder and a subfolder:
TotalMinutes Olaf Methode: 79,6190819866667
TotalMinutes krzydoug Methode: 64,8277968883333
TotalMinutes both Mixed: 66,5072204416667

So i deceide to go with a mix from both:

$Shares = Get-SmbShare | Get-SmbShareAccess 
$Folderlist = $Null
$Folderlist = @()

$DrivestoSkip = @("c","d","e")
$RootFolderstoSkip = @("W:\Installation")
$FolderstoSkip = @("W:\Installation")


$ExcludePattern = 
@(
    foreach ($element in $FolderstoSkip) {
        [regex]::Escape($element)
    }
) -join '|'

$gciparams = @{
    Directory     = $true
    ErrorAction   = 'SilentlyContinue'
    ErrorVariable = '+errors'
}

$Drives = Get-PSDrive | Where {$_.Provider -match "File"}
foreach ($Drive in $Drives) 
{
   Write-Host "Discovery Subfolders of Drive $($Drive.Root)"
   if ($DrivestoSkip -notcontains $Drive.Name) 
   { $folderlist = Get-ChildItem -Path $Drive.Root @gciparams | Where-Object Name -NotIn $RootFolderstoSkip | Get-ChildItem -Recurse @gciparams | Where-Object -Property FullName -NotMatch -Value $ExcludePattern }
}
1 Like

Please do not repost your answer when it gets held back. Give the moderators/admins a little time to check.

1 Like