How can I exclude multiple folders using Get-ChildItem -exclude?

I need to generate a configuration file for our Pro/Engineer CAD system. I need a recursive list of the folders from a particular drive on our server. However I need to EXCLUDE any folder with 'ARCHIVE' in it including the various different cases.

I've written the following which works except it doesn't exclude the folders !!

$folder = "T:\Drawings\Design\*" $raw_txt = "T:\Design Projects\Design_Admin\PowerShell\raw.txt" $search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro" $archive = *archive*,*Archive*,*ARCHIVE* Get-ChildItem -Path $folder -Exclude $archive -Recurse | where {$_.Attributes -match 'Directory'} | ForEach-Object {$_.FullName} > $search_pro 
1

18 Answers

My KISS approach to skip some folders is chaining Get-ChildItem calls. This excludes root level folders but not deeper level folders if that is what you want.

Get-ChildItem -Exclude folder1,folder2 | Get-ChildItem -Recurse | ... 
  • Start excluding folders you don't want
  • Then do the recursive search with non desired folders excluded.

What I like from this approach is that it is simple and easy to remember. If you don't want to mix folders and files in the first search a filter would be needed.

4

I'd do it like this:

Get-ChildItem -Path $folder -r | ? { $_.PsIsContainer -and $_.FullName -notmatch 'archive' } 

Note that -notmatch accepts a Regular Expression:

6

I apologize if this answer seems like duplication of previous answers. I just wanted to show an updated (tested through POSH 5.0) way of solving this. The previous answers were pre-3.0 and not as efficient as modern solutions.

The documentation isn't clear on this, but Get-ChildItem -Recurse -Exclude only matches exclusion on the leaf (Split-Path $_.FullName -Leaf), not the parent path (Split-Path $_.FullName -Parent). Matching the exclusion will just remove the item with the matching leaf; Get-ChildItem will still recurse into that leaf.

In POSH 1.0 or 2.0

Get-ChildItem -Path $folder -Recurse | ? { $_.PsIsContainer -and $_.FullName -inotmatch 'archive' } 

Note: Same answer as @CB.

In POSH 3.0+

Get-ChildItem -Path $folder -Directory -Recurse | ? { $_.FullName -inotmatch 'archive' } 

Note: Updated answer from @CB.

Multiple Excludes

This specifically targets directories while excluding leafs with the Exclude parameter, and parents with the ilike (case-insensitive like) comparison:

#Requires -Version 3.0 [string[]]$Paths = @('C:\Temp', 'D:\Temp') [string[]]$Excludes = @('*archive*', '*Archive*', '*ARCHIVE*', '*archival*') $files = Get-ChildItem $Paths -Directory -Recurse -Exclude $Excludes | %{ $allowed = $true foreach ($exclude in $Excludes) { if ((Split-Path $_.FullName -Parent) -ilike $exclude) { $allowed = $false break } } if ($allowed) { $_ } } 

Note: If you want your $Excludes to be case-sensitive, there are two steps:

  1. Remove the Exclude parameter from Get-ChildItem.
  2. Change the first if condition to:
    • if ($_.FullName -clike $exclude) {

Note: This code has redundancy that I would never implement in production. You should simplify this quite a bit to fit your exact needs. It serves well as a verbose example.

6

The exclusion pattern should be case-insensitive, so you shouldn't have to specify every case for the exclusion.

That said, the -Exclude parameter accepts an array of strings, so as long as you define $archive as such, you should be set.

$archive = ("*archive*","*Archive*","*ARCHIVE*");

You also should drop the trailing asterisk from $folder - since you're specifying -recurse, you should only need to give the top-level folder.

$folder = "T:\Drawings\Design\"

Fully revised script. This also changes how you detect whether you've found a directory, and skips the Foreach-Object because you can just pull the property directly & dump it all to the file.

$folder = "T:\Drawings\Design\"; $raw_txt = "T:\Design Projects\Design_Admin\PowerShell\raw.txt"; $search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro"; $archive = ("*archive*","*Archive*","*ARCHIVE*"); Get-ChildItem -Path $folder -Exclude $archive -Recurse | where {$_.PSIsContainer} | select-Object -expandproperty FullName |out-file $search_pro 
13

I know this is quite old - but searching for an easy solution, I stumbled over this thread... If I got the question right, you were looking for a way to list more than one directory using Get-ChildItem. There seems to be a much easier way using powershell 5.0 - example

Get-ChildItem -Path D:\ -Directory -Name -Exclude tmp,music chaos docs downloads games pics videos 

Without the -Exclude clause, tmp and music would still be in that list. If you don't use -Name the -Exclude clause won't work, because of the detailed output of Get-ChildItem. Hope this helps some people that are looking for an easy way to list all directory names without certain ones.

1

You can exclude like this, the regex 'or' symbol, assuming a file you want doesn't have the same name as a folder you're excluding.

$exclude = 'dir1|dir2|dir3' ls -r | where { $_.fullname -notmatch $exclude } ls -r -dir | where fullname -notmatch 'dir1|dir2|dir3' 

VertigoRay, in his answer, explained that -Exclude works only at the leaf level of a path (for a file the filename with path stripped out; for a sub-directory the directory name with path stripped out). So it looks like -Exclude cannot be used to specify a directory (eg "bin") and exclude all the files and sub-directories within that directory.

Here's a function to exclude files and sub-directories of one or more directories (I know this is not directly answering the question but I thought it might be useful in getting around the limitations of -Exclude):

$rootFolderPath = 'C:\Temp\Test' $excludeDirectories = ("bin", "obj"); function Exclude-Directories { process { $allowThrough = $true foreach ($directoryToExclude in $excludeDirectories) { $directoryText = "*\" + $directoryToExclude $childText = "*\" + $directoryToExclude + "\*" if (($_.FullName -Like $directoryText -And $_.PsIsContainer) ` -Or $_.FullName -Like $childText) { $allowThrough = $false break } } if ($allowThrough) { return $_ } } } Clear-Host Get-ChildItem $rootFolderPath -Recurse ` | Exclude-Directories 

For a directory tree:

C:\Temp\Test\ | ├╴SomeFolder\ | | | └╴bin (file without extension) | └╴MyApplication\ | ├╴BinFile.txt ├╴FileA.txt ├╴FileB.txt | └╴bin\ | └╴Debug\ | └╴SomeFile.txt 

The result is:

C:\Temp\Test\ | ├╴SomeFolder\ | | | └╴bin (file without extension) | └╴MyApplication\ | ├╴BinFile.txt ├╴FileA.txt └╴FileB.txt 

It excludes the bin\ sub-folder and all its contents but does not exclude files Bin.txt or bin (file named "bin" without an extension).

1

The simplest short form to me is something like:

#find web forms in my project except in compilation directories (gci -recurse -path *.aspx,*.ascx).fullname -inotmatch '\\obj\\|\\bin\\' 

And if you need more complex logic then use a filter:

 filter Filter-DirectoryBySomeLogic{ param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $fsObject, [switch]$exclude ) if($fsObject -is [System.IO.DirectoryInfo]) { $additional_logic = $true ### replace additional logic here if($additional_logic){ if(!$exclude){ return $fsObject } } elseif($exclude){ return $fsObject } } } gci -Directory -Recurse | Filter-DirectoryBySomeLogic | .... 

Based on @NN_ comment on @Guillem answer, I came up with the below code. This allows you to exclude folders and files:

Get-ChildItem -Exclude 'folder-to-exclude','second-folder-exclude' | foreach { Get-ChildItem -Path $_ -Exclude 'files-to-exclude','*.zip','*.mdmp','*.out*','*.log' -Recurse | Select-String -Pattern 'string-to-look-for' -List } 

You could also do this in a single statement:

$j = "Somepath" $files = Get-ChildItem -Path $j -Include '*.xlsx','*.zip' -Recurse -ErrorAction SilentlyContinue –File | ? {$_.Directory -notlike "$j\donotwantfoldername"} 
1

This is how I did it:

Get-ChildItem -Recurse -Name | ? {$_ -notmatch 'node_modules' } 

This lists the full path of every file recursively that does NOT contains node_modules in its path. You should obviously change node_modules with any string you want to filter

#For brevity, I didn't define a function. #Place the directories you want to exclude in this array. #Case insensitive and exact match. So 'archive' and #'ArcHive' will match but 'BuildArchive' will not. $noDirs = @('archive') #Build a regex using array of excludes $excRgx = '^{0}$' -f ($noDirs -join ('$|^')) #Rather than use the gci -Recurse option, use a more #performant approach by not processing the match(s) as #soon as they are located. $cmd = { Param([string]$Path) Get-ChildItem $Path -Directory | ForEach-Object { if ($_.Name -inotmatch $excRgx) { #Recurse back into the scriptblock Invoke-Command $cmd -ArgumentList $_.FullName; #If you want all directory info change to return $_ return $_.FullName } } } #In this example, start with the current directory $searchPath = . #Start the Recursion Invoke-Command $cmd -ArgumentList $searchPath 

I wanted a solution that didn't involve looping over every single item and doing ifs. Here's a solution that is just a simple recursive function over Get-ChildItem. We just loop and recurse over directories.

 function Get-RecurseItem { [Cmdletbinding()] param ( [Parameter(ValueFromPipeline=$true)][string]$Path, [string[]]$Exclude = @(), [string]$Include = '*' ) Get-ChildItem -Path (Join-Path $Path '*') -Exclude $Exclude -Directory | ForEach-Object { @(Get-ChildItem -Path (Join-Path $_ '*') -Include $Include -Exclude $Exclude -File) + `` @(Get-RecurseItem -Path $_ -Include $Include -Exclude $Exclude) } } 
$CurrentPath = (Get-Location -PSProvider FileSystem).ProviderPath # Or your favorite path $IncludeNames = "okFolder1", "okFolder2" # Items names to search $ExcludeNames = "koFolder1", "koFolder2" # Items names not to search $depth = 3 # Max level of depth to search $FoldersToRemove = Get-ChildItem .\ -include $IncludeNames -Recurse -Depth $depth -Attributes D # If you want only directories, change it as you desire | ? { $_.fullname -inotmatch ($ExcludeNames -join '|') } #case insensitive or use -cnotmatch for case sensitive comparison | foreach {$_.fullname} # If you want to project only full path information of matches 

Here is another method using a remote server. The task here is to get a list of folders but exclude a number of well known folders on the remote server's C: drive. The final variable $AllFolders contains the result.

$Server = "<ServerName>" $TopFolder = "\\$Server\C$" $Exclude = @("*Inetpub*") $Exclude += "*Program Files*" $Exclude += "*Program Files (x86)*" $Exclude += "*Windows*" $ServerFolders = Get-ChildItem -Path $TopFolder -Exclude $Exclude | where {$_.PSIsContainer} ForEach ($ServerFolder in $ServerFolders) { $CurrentFolders = Get-ChildItem -path $ServerFolder -recurse | Where-Object { $_.PSIsContainer } $AllFolders = $AllFolders + $CurrentFolders } 

Simpliest form in my opinion. Use -NotMatch on Fullname. Yes, it needs a recent version of PowerShell because I use -Directory.

$folder = "T:\Drawings\Design\*" $search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro" $archive = 'archive' Get-ChildItem -Path $folder -Directory | Where-Object Fullname -NotMatch $archive | Select-Object Fullname | Out-File $search_pro 

I needed to exclude specific paths, not just directories of the same name anywhere in the tree, so I built on Jason Brower's answer to match directory paths instead of their names.

Solutions like Get-Childitem filespec -Recurse | Where-Object {$_ -excludecondition} do work, but they unnecessarily look into the excluded folders before dismissing them which can get expensive. (With pipes: "Filter left, process right")

$strSearchPath = 'D:\Project' # Files to search for $arFilePatterns = @( '*.ps?', '*.cmd' ) # Directories to skip # Example: you got 'Archive', 'Archive.old', 'Archive.bak' and want to include only 'Archive' in the search # (think) exact matches $arSkipDirs = @( 'D:\Project\Archive.old', 'D:\Project\Archive.bak' ) # (think) wildcard to the right <# $arSkipDirs = @( 'D:\Project\Archive.' ) #> Function ListDirsSkipSome ($strPath, $strExcludeRegEx) { Get-ChildItem -Path $strPath -Directory | ForEach-Object { if ($_.FullName -inotmatch $strExcludeRegEx) { # recurse down the tree ListDirsSkipSome $_.FullName $strExcludeRegEx return $_.FullName } } } #Build a regex using array of excludes # exact matches $strRegEx = '^{0}$' -f (($arSkipDirs | ForEach-Object { [regex]::Escape($_) }) -join ('$|^')) # wildcards to the right #$strRegEx = '^{0}' -f (($arSkipDirs | ForEach-Object { [regex]::Escape($_) }) -join ('|^')) # include root of search path $arSearchDirs = @($strSearchPath) # add list of directories excluding some $arSearchDirs += ListDirsSkipSome $strSearchPath $strRegEx # save current directory $strPWD = (Get-Location).Path # find files in listed dirs # set type in case there is only 1 result [array]$arSearchResult = $arSearchDirs | ForEach-Object { # dive into each directory Set-Location -Path $_ # list files matching patterns (Get-ChildItem -File -Path $arFilePatterns).FullName } # return to previous directory Set-Location -Path $strPWD $arSearchResult 
1

may be in your case you could reach this with the following:

 mv excluded_dir ..\ ls -R mv ..\excluded_dir . 
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like