Tanmoy De
Tanmoy De

Reputation: 31

In PowerShell Get-ChildItem -Exclude is not working with -Recurce parameter

enter image description hereI want to search all directory recursively except the directory which starts with _(underscore).

Ex: _archive, _DO_NOT_DELETE, _processing, etc.

I used below code and it is not working. It seems exclude will not work with recurse . Any ideas?

$exclude = @("_*")
$source = "C:\code\Powershell\delete empty folders\New folder\" 
$allitems = Get-ChildItem $source -Directory -Exclude $exclude -Recurse
foreach($myItem in $allitems)
{
  echo $myItem.fullname
} 

Output:

C:\code\Powershell\delete empty folders\New folder\New folder\New folder
C:\code\Powershell\delete empty folders\New folder\New folder\New folder\New folder
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (2)
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (3)
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (3)\New folder
C:\code\Powershell\delete empty folders\New folder\_archive\New folder

So, as I told before I want to avoid the directory started with underscore, hence the last line should not come.

Even I used below code as well, still same output:

$exclude = @("_*")
$source = "C:\code\Powershell\delete empty folders\New folder\"
$allitems = Get-ChildItem $source -Recurse -Directory |
            Where {$_.FullName -notlike $exclude}
foreach($myItem in $allitems)
{
    echo $myItem.fullname
}

Upvotes: 3

Views: 1180

Answers (3)

vonPryz
vonPryz

Reputation: 24091

What happens here is that Get-ChildItem's -exclude parameter works based on item's name property. When wildcards are being used, the name must match the non-wildcarded parts. This doesn't happen when your directory name begins with a space. Let's see an example:

# Create two files, one starts with a space, the other doesn't
Set-Content -Path " data1.txt" -Value $null
Set-Content -Path "data2.txt" -Value $null

gci -name "data*"
 data2.txt

gci -name " data*"
 data1.txt

The result is a single match, tough a wildcard was used. The reason is that data1.txt's first character is a space and that doesn't match parameter's first character, namely d.

The -exclude is notorious for being tricky. It is often easier to work around by piping results to where-object. Like so,

Get-ChildItem $source -Directory -recurse | ? {$_.fullname -NotMatch "\\\s*_"} | % { $_.fullname }
# "\\\s*_" is regex for \, any amount of whitespace, _ 
C:\code\Powershell\delete empty folders\New folder\New folder
C:\code\Powershell\delete empty folders\New folder\New folder\New folder
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (2)
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (3)
C:\code\Powershell\delete empty folders\New folder\New folder\New folder\New folder
C:\code\Powershell\delete empty folders\New folder\New folder\New folder (3)\New folder

Get-ChildItem $source -Directory -recurse | ? {$_.fullname -Match "\\\s*_"} | % { $_.fullname }
C:\code\Powershell\delete empty folders\New folder\ _archive
C:\code\Powershell\delete empty folders\New folder\ _archive\New folder

Note that the matching needs to evaluate FullName property. Just using Name would match only the leaf level.

Upvotes: 4

boxdog
boxdog

Reputation: 8442

As @Paxz says in the comments, the command is working as intended, just not as you'd hoped. It is definitely excluding the folders starting with _ from the output, but this does not apply to the -Recurse switch, so it still looks inside those folders for other items, which it displays (if their name does not start with _).

You can use your own filter to get what you want:

Get-ChildItem -Path $source -Directory -recurse |
    Where-Object {$_.FullName -notlike "*\_*"} |
        Format-Table FullName

Note that this still finds all the folders with _, but just discards them (so, no performance improvement). Also, using the simpler filter: -notlike "*_*" would exclude any folder with an underscore in the middle or end of its name (e.g. Hello_World), not just the beginning.

Upvotes: 2

Paxz
Paxz

Reputation: 3046

Haven't found an answer to the first attempt. But if you want to go with Where-Object you have to change your Filter from _* to *_*.

This is because .Fullname displays the full path, so alot more characters before the actual _ of the Path.

$exclude = @("*_*")
$source = "C:\code\Powershell\delete empty folders\New folder\"
$allitems = Get-ChildItem $source -Recurse -Directory | Where {$_.FullName -notlike $exclude}
$allitems.Fullname

Should give you the expected output.

Upvotes: 2

Related Questions