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

Mat*_*ker 9 c# algorithm .net-4.0

我最近需要枚举整个文件系统,以查找特定类型的文件以进行审计.由于对要扫描的文件系统的权限有限,这导致我遇到几个例外.其中,最流行的是UnauthorizedAccessException以及我的懊恼,PathTooLongException.

这些通常不会成为问题,除了它们使IEnumerable无效,从而阻止我完成扫描.

Mat*_*ker 16

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

// 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法非常简单.

//This code is public domain
var root = new DirectoryInfo(@"c:\wherever");
var searchPattern = @"*.txt";
var searchOption = SearchOption.AllDirectories;
var enumerable = new FileSystemEnumerable(root, searchPattern, searchOption);
Run Code Online (Sandbox Code Playgroud)

如果人们发现它有用,他们可以自由使用它.

  • 我断言,截至 2015 年 7 月 14 日,上述答案中的代码属于公共领域,具有授予的所有权利和特权。 (2认同)
  • 我发现我需要捕获`System.IO.IOException`,因为我正在走一个网络驱动器,它有一个远程到本地的simlink目录,有这样的simlink扩展规则[在当前机器上禁用](http:// blogs .msdn.com/b /俊峰/存档/ 2012/05/07 /的象征 - 连接 - 不能待随后-因为-其型是-disabled.aspx).我相应地调整了你的答案.如果它遇到指向祖先文件夹的simlink,它也会无限地递归; 在我的情况下,我只是忽略了具有`FileAttributes.ReparsePoint`属性的dirs,但这可能不够优雅,无法得到一般答案 (2认同)