C# DirectoryInfo.EnumerateFiles(…)导致UnauthorizedAccessException(和其他异常)

C# DirectoryInfo.EnumerateFiles(…)导致UnauthorizedAccessException(和其他异常),c#,algorithm,.net-4.0,C#,Algorithm,.net 4.0,我最近需要枚举整个文件系统,以查找特定类型的文件,以便进行审计。由于对要扫描的文件系统的权限有限,这导致我遇到了几个异常。其中,最普遍的是,我很懊恼 这些通常不是问题,除非它们使IEnumerable无效,使我无法完成扫描。为了解决这个问题,我创建了一个替换文件系统枚举器。虽然它可能并不完美,但它执行得相当快,并捕获了我遇到的两个异常。它将找到与传递给它的搜索模式匹配的任何目录或文件 // This code is public domain using System; using System

我最近需要枚举整个文件系统,以查找特定类型的文件,以便进行审计。由于对要扫描的文件系统的权限有限,这导致我遇到了几个异常。其中,最普遍的是,我很懊恼


这些通常不是问题,除非它们使IEnumerable无效,使我无法完成扫描。

为了解决这个问题,我创建了一个替换文件系统枚举器。虽然它可能并不完美,但它执行得相当快,并捕获了我遇到的两个异常。它将找到与传递给它的搜索模式匹配的任何目录或文件

// This code is public domain
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using log4net;

public class FileSystemEnumerable : IEnumerable<FileSystemInfo>
{
    private ILog _logger = LogManager.GetLogger(typeof(FileSystemEnumerable));

    private readonly DirectoryInfo _root;
    private readonly IList<string> _patterns;
    private readonly SearchOption _option;

    public FileSystemEnumerable(DirectoryInfo root, string pattern, SearchOption option)
    {
        _root = root;
        _patterns = new List<string> { pattern };
        _option = option;
    }

    public FileSystemEnumerable(DirectoryInfo root, IList<string> patterns, SearchOption option)
    {
        _root = root;
        _patterns = patterns;
        _option = option;
    }

    public IEnumerator<FileSystemInfo> GetEnumerator()
    {
        if (_root == null || !_root.Exists) yield break;

        IEnumerable<FileSystemInfo> matches = new List<FileSystemInfo>();
        try
        {
            _logger.DebugFormat("Attempting to enumerate '{0}'", _root.FullName);
            foreach (var pattern in _patterns)
            {
                _logger.DebugFormat("Using pattern '{0}'", pattern);
                matches = matches.Concat(_root.EnumerateDirectories(pattern, SearchOption.TopDirectoryOnly))
                                 .Concat(_root.EnumerateFiles(pattern, SearchOption.TopDirectoryOnly));
            }
        }
        catch (UnauthorizedAccessException)
        {
            _logger.WarnFormat("Unable to access '{0}'. Skipping...", _root.FullName);
            yield break;
        }
        catch (PathTooLongException ptle)
        {
            _logger.Warn(string.Format(@"Could not process path '{0}\{1}'.", _root.Parent.FullName, _root.Name), ptle);
            yield break;
        } catch (System.IO.IOException e)
        {
            // "The symbolic link cannot be followed because its type is disabled."
            // "The specified network name is no longer available."
            _logger.Warn(string.Format(@"Could not process path (check SymlinkEvaluation rules)'{0}\{1}'.", _root.Parent.FullName, _root.Name), e);
            yield break;
        }


        _logger.DebugFormat("Returning all objects that match the pattern(s) '{0}'", string.Join(",", _patterns));
        foreach (var file in matches)
        {
            yield return file;
        }

        if (_option == SearchOption.AllDirectories)
        {
            _logger.DebugFormat("Enumerating all child directories.");
            foreach (var dir in _root.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
            {
                _logger.DebugFormat("Enumerating '{0}'", dir.FullName);
                var fileSystemInfos = new FileSystemEnumerable(dir, _patterns, _option);
                foreach (var match in fileSystemInfos)
                {
                    yield return match;
                }
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

如果人们觉得它有用,他们可以自由使用它。

还有另一种方法,管理您自己的枚举迭代:

IEnumerator<string> errFiles=Directory.EnumerateFiles(baseDir, "_error.txt", SearchOption.AllDirectories).GetEnumerator();
while (true)
{
    try
    {
        if (!errFiles.MoveNext())
            break;
        string errFile = errFiles.Current;
        // processing
    } catch (Exception e)
    {
        log.Warn("Ignoring error finding in: " + baseDir, e);
    }
}
IEnumerator errFiles=Directory.EnumerateFiles(baseDir,“\u error.txt”,SearchOption.AllDirectories).GetEnumerator();
while(true)
{
尝试
{
如果(!errFiles.MoveNext())
打破
字符串errFile=errFiles.Current;
//加工
}捕获(例外e)
{
log.Warn(“忽略“+baseDir,e”中的错误查找);
}
}

“如果人们觉得它有用,他们可以自由使用。”:事实并非如此。您在SA 3的CC(知识共享)下努力,这使得它实际上很容易使用。您可以在注释中的代码片段中明确地声明“公共域”(copyleft)或zlib许可证(最弱的版权许可证)。谢谢。我声明,截至2015年7月14日,上述答案中的代码是公共域,具有授予的所有权利和特权。我发现我需要捕获
System.IO.IOException
,以防我在网络驱动器上行走,该网络驱动器具有从远程到本地的simlink目录以及此类simlink扩展规则。我相应地调整了你的答案。如果遇到指向祖先文件夹的simlink,它也会无限递归;在我的例子中,我只是忽略了属性为
FileAttributes.ReparsePoint
的dir,但这对于一般回答来说可能不够优雅,这对我很有帮助。我对IMHO做了一点小小的改进,将多个构造函数简化为一个同样简单的版本public filesystemnumerable(DirectoryInfo root、SearchOption选项、params string[]patterns){{u root=root;_patterns=patterns.ToList();_option=option;}`通过将
\u root.EnumerateDirectories()
链接到
\u root.EnumerateFiles()
(强制按类型将输出“分组”在一起?)再加上在
GetEnumerator()
末尾对
\u root.EnumerateDirectories()
的另一个调用,在较低的级别上,我相信这将导致目录中的每个对象调用三次,这对于具有大量子对象的目录是不好的。这是因为所有三个
Enumerate*s()
方法都会执行完整的目录扫描,而不管搜索对象类型。我解决这个问题已有一段时间了,但如果我没记错的话,问题是目录。Enumerate文件(…)正在引发异常,因为它在返回枚举数之前会预先检查目录中的所有内容。我很确定,在这种情况下不会。当使用GetFiles()时会出现这种情况,因为所有文件都是迭代和收集的,但对于EnumerateFiles(),权限断言会在MoveNext()上单独出现。由于try and while错误,导致记录并移动到下一个。啊,是的,我相信您在解决权限问题方面是正确的。虽然,据我所知,它们并没有捕获MoveNext()期间可能发生的所有错误。实例的PathTooLongException未被自动捕获和处理。我对文件系统枚举器的内部实现的理解是,任何确实发生且未在枚举器实例中处理的异常都会导致它失效。也就是说,我已经有好几年没有深入研究和验证过这个行为了。我只想说,我认为上面Brubaker先生提供的
filesystemnumerable
类非常棒,因此我想鼓励进一步使用/开发它。为此,我将其发布为一个只包含一个文件的GitHub repo,以及一个README.md。我链接回这个StackOverflow帖子。请查找发布在以下位置的课程:欢迎所有人参与回购/提交问题/创建拉取请求。谢谢大家!布赖恩·哈特
IEnumerator<string> errFiles=Directory.EnumerateFiles(baseDir, "_error.txt", SearchOption.AllDirectories).GetEnumerator();
while (true)
{
    try
    {
        if (!errFiles.MoveNext())
            break;
        string errFile = errFiles.Current;
        // processing
    } catch (Exception e)
    {
        log.Warn("Ignoring error finding in: " + baseDir, e);
    }
}