10

I'm enumerating all files in a directory so that I can process them later. I want to exclude hidden and system files.

This is what I have so far:

IEnumerable<IGrouping<string, string>> files;

files = Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)
       .Where(f => (new FileInfo(f).Attributes & FileAttributes.Hidden & FileAttributes.System) == 0)
       .GroupBy(Path.GetDirectoryName);

However if I look at the results I am still getting hidden and system files included:

foreach (var folder in files)
{
    foreach (var file in folder)
    {
        // value for file here still shows hidden/system file paths
    }
}

I found this question which has a similar example from Jerome but I couldn't even get that to compile.

What am I doing wrong?

Community
  • 1
  • 1
Equalsk
  • 7,954
  • 2
  • 41
  • 67
  • 3
    `FileAttributes.Hidden & FileAttributes.System` always equals 0. – Damien_The_Unbeliever Jan 05 '16 at 11:54
  • Oh, I didn't realise that. I tried `(new FileInfo(f).Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)` which works but I struggled to figure out how to include system files in this as well. – Equalsk Jan 05 '16 at 11:56
  • 1
    Adding to that, if you are going to create a new FileInfo, start from DirectoryInfo so you get FileInfo objects from the start. As for the filter, try `fi.Attributes & (FileAttributes.Hidden | FileAttributes.System)) == 0`. First create the mask you want `FileAttributes.Hidden | FileAttributes.System` and *then* do a bitwise AND. The folder path is also available through `FileInfo.DirectoryName` – Panagiotis Kanavos Jan 05 '16 at 11:58

3 Answers3

10
.Where(f => (new FileInfo(f).Attributes & FileAttributes.Hidden & FileAttributes.System) == 0)

Since FileAttributes values are flags, they are disjunctive on the bit level, so you can combine them properly. As such, FileAttributes.Hidden & FileAttributes.System will always be 0. So you’re essentially checking for the following:

(new FileInfo(f).Attributes & 0) == 0

And that will always be true since you are removing any value with the & 0 part.

What you want to check is whether the file has neither of those flags, or in other words, if there are no common flags with the combination of both:

.Where(f => (new FileInfo(f).Attributes & (FileAttributes.Hidden | FileAttributes.System)) == 0)

You can also use Enum.HasFlag to make this a bit better understandable:

.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System))
poke
  • 369,085
  • 72
  • 557
  • 602
3

You can simplify your code a lot by using DirectoryInfo and FileInfo, eg:

var mask= FileAttributes.Hidden | FileAttributes.System;

var di=new DirectoryInfo(sourcePath);
var files=di.EnumerateFiles("*", SearchOption.AllDirectories)
            .Where(fi=>(fi.Attributes & mask) == 0)
            .GroupBy(fi=>fi.DirectoryName);

Your original code tried to do a bitwise AND between flags that had no common bits, so it returned 0. As a result, the bitwise AND with Attributes also returned 0.

The mask you want to check against is System or Hidden ie FileAttributes.Hidden | FileAttributes.System. Calculating this in advance simply makes for somewhat cleaner code

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • In the Where clause, the bitwise & expression needs to be encased in parentheses, since == has a higher precedence than &, i.e. `.Where(fi => (fi.Attributes & mask) == 0)` – Tim Trout Jun 14 '19 at 18:26
1

You can use FileSystemInfo.Attributes.HasFlag:

DirectoryInfo dir = new DirectoryInfo( sourcePath );
var files = dir.EnumerateFiles("*", SearchOption.AllDirectories)
    .Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden) && !f.Attributes.HasFlag(FileAttributes.System))
    .GroupBy(f => f.DirectoryName);
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939