Reputation: 245046
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
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
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
Reputation: 21716
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 :)
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 :)
Get-ChildItem | Where-Object {$_ -is [IO.FileInfo]}
Or:
gci | ? {$_ -is [io.fileinfo]}
Get-ChildItem | Where-Object {$_ -is [IO.DirectoryInfo]}
Or:
gci | ? {$_ -is [io.directoryinfo]}
Upvotes: 11
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