svick
svick

Reputation: 245046

Exclude directories in PowerShell

I want to exclude all directories from my search in PowerShell. Both FileInfo and DirectoryInfo contain Attributtes property that seems to be exactly what I want, but I wasn't able to find out how to filter based on it. Both

ls | ? { $_.Attributes -ne 'Direcory' }
ls | ? { $_.Attributes -notcontains 'Direcory' }

didn't work. How can I do this?

Upvotes: 26

Views: 22434

Answers (4)

isidroco
isidroco

Reputation: 43

Timings for different approaches, times of 5000 itarations on a short dir / 25 iterations on System32 dir. Measuring is done using:

(measure-command { for ($i=0;$i -lt 5e3;$i++) {       
  $files= gci | ? { !$_.PSIsContainer }   # this is target to measure
} } ).totalmilliseconds

Results from slowest to fastest (all lines have same end result):

$files= gi *.* | ? { !$_.PSIsContainer }  #5000 iterations / 25long = 15.2sec / 22s
$files= foreach ($file in gi *.*) { if ($file.mode -notmatch 'd') { $file } }   # 11.8s / 20s
$files= gci | ? { !($_.Attributes -band [IO.FileAttributes]::Directory) }  # 8.9s  /  10.7s
$files= Get-ChildItem | Where-Object {$_.mode -notmatch 'd'}  # 8.8s  / 10.6s
$files= gci | ? { !$_.PSIsContainer }  # 7.8s  / 9.8s
$files= Get-ChildItem | ? {$_ -isnot [IO.DirectoryInfo]}  # 7.6s  /  9.6s
$files= gci | Where-Object {$_ -is [IO.FileInfo]}  # 7.6s  / 9.6s
$files= foreach ($file in gci *.*) { if ($file.mode -notmatch 'd') { $file } }    #7.3s  / 12.4s
$files= @( foreach ($file in gci) { if ($file.mode -notmatch 'd') { $file } } ) #3.7s  /  6.4s
$files= foreach ($file in gci) { if ($file.mode -notmatch 'd') { $file } }  # 3.7s  /  6.4s

Notice that specifying "*.*" will almost double process time. That's why GCI without parameters is fastest than GI which must use *.* parameter.

Upvotes: 0

Joey
Joey

Reputation: 354854

You can use the PSIsContainer property:

gci | ? { !$_.PSIsContainer }

Your approach would work as well, but would have to look like this:

gci | ? { !($_.Attributes -band [IO.FileAttributes]::Directory) }

as the attributes are an enum and a bitmask.

Or, for your other approach:

gci | ? { "$($_.Attributes)" -notmatch "Directory" }

This will cause the attributes to be converted to a string (which may look like "Directory, ReparsePoint"), and on a string you can use the -notmatch operator.

PowerShell v3 finally has a -Directory parameter on Get-ChildItem:

Get-ChildItem -Directory
gci -ad

Upvotes: 25

Alain O'Dea
Alain O'Dea

Reputation: 21716

Exclude directories in PowerShell:

Get-ChildItem | Where-Object {$_ -isnot [IO.DirectoryInfo]}

Or it's terse, but harder to read version:

gci | ? {$_ -isnot [io.directoryinfo]}

Credit goes to @Joey for his insightful comment using the -is operator :)

However

Technically, I prefer including only Files or only Directories since excluding can lead to unexpected results as Get-ChildItem can return more than just files and directories :)

Include just Files:

Get-ChildItem | Where-Object {$_ -is [IO.FileInfo]}

Or:

gci | ? {$_ -is [io.fileinfo]}

Include just Directories:

Get-ChildItem | Where-Object {$_ -is [IO.DirectoryInfo]}

Or:

gci | ? {$_ -is [io.directoryinfo]}

Upvotes: 11

Anton I. Sipos
Anton I. Sipos

Reputation: 3633

You can also filter out directories by looking at their type directly:

ls | ?{$_.GetType() -ne [System.IO.DirectoryInfo]}

Directories are returned by get-childitem (or ls or dir) of type System.IO.DirectoryInfo, and files are of type System.IO.FileInfo. When using the types as literals in Powershell you need to put them in brackets.

Upvotes: 6

Related Questions