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);
}
}