2012-10-29 41 views
9

我最近需要枚舉整個文件系統以查找特定類型的文件以進行審計。由於在要掃描的文件系統上擁有有限的權限,這使我遇到了幾個例外情況。其中,最普遍的是UnauthorizedAccessException,對我的懊惱更是如此,PathTooLongExceptionDirectoryInfo.EnumerateFiles(...)導致UnauthorizedAccessException(以及其他例外)

這些通常不會成爲一個問題,除非它們使IEnumerable無效,從而阻止我能夠完成掃描。

回答

14

爲了解決這個問題,我創建了一個替代文件系統枚舉器。雖然它可能不完美,但它表現相當快,並陷入我遇到的兩個例外。它會查找與傳遞給它的搜索模式相匹配的任何目錄或文件。

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

這個用法很簡單。

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

人們可以自由使用它,如果他們覺得有用。

+0

「人們可以自由使用它,如果他們覺得有用。」 :這不太真實。你在CC BY SA 3中發揮作用,(創意共同體),事實上它使用起來很微妙。您可以在評論中的代碼段中明確聲明「公有領域」(copyleft)或zlib許可(最弱版權許可)。謝謝。 –

+1

我聲稱上述截至2015年7月14日的答案中的代碼是具有授予的所有權利和特權的公有領域。 –

+1

我發現我需要趕上'System.IO.IOException'的情況下,我行走一個網絡驅動器,有一個遠程到本地的simlink目錄,這種simlink擴展規則[在當前機器上禁用](http:// blogs.msdn.com/b/junfeng/archive/2012/05/07/the-symbolic-link-cannot-be-followed-because-its-type-is-disabled.aspx)。我相應地調整了你的答案。如果遇到指向祖先文件夾的simlink,它也會無限遞歸;在我的情況下,我只是忽略了具有FileAttributes.ReparsePoint屬性的dirs,但是這對於一般的答案可能不夠優雅 – RJFalconer