2012-10-29 1 views
9

J'ai récemment eu besoin d'énumérer un système de fichiers complet à la recherche de types spécifiques de fichiers à des fins d'audit. Cela m'a amené à exécuter plusieurs exceptions en raison d'autorisations limitées sur le système de fichiers à analyser. Parmi eux, les plus répandus ont été UnauthorizedAccessException et beaucoup à mon grand regret, PathTooLongException.DirectoryInfo.EnumerateFiles (...) provoque UnauthorizedAccessException (et d'autres exceptions)

Cela ne poserait normalement pas de problème, sauf qu'ils invalideraient IEnumerable, ce qui m'empêcherait de terminer l'analyse.

Répondre

14

Afin de résoudre ce problème, j'ai créé un remplaçant de système de fichiers. Bien qu'il ne soit pas parfait, il fonctionne assez rapidement et piège les deux exceptions que j'ai rencontrées. Il trouvera tous les répertoires ou fichiers qui correspondent au modèle de recherche qui lui est transmis.

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

L'utilisation est assez simple.

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

Les gens sont libres de l'utiliser s'ils le trouvent utile.

+0

"Les gens sont libres de l'utiliser s'ils le trouvent utile." : ce n'est pas tout à fait vrai. vous exercez sous CC BY SA 3, (creative commons) ce qui rend délicate à utiliser en fait. Vous pouvez indiquer explicitement "public domain" (copyleft) ou licence zlib (licence de copyright la plus faible), dans votre extrait de code dans un commentaire. Merci. –

+1

J'affirme que le code dans la réponse ci-dessus à compter du 14 Juillet 2015 est du domaine public avec tous les droits et privilèges qui accorde. –

+1

J'ai trouvé que j'avais besoin d'attraper 'System.IO.IOException' pour les situations où je marche sur un lecteur réseau qui a un répertoire de lien réseau local à distant avec de telles règles d'expansion de lien réseau [désactivé sur la machine actuelle] (http: // blogs.msdn.com/b/junfeng/archive/2012/05/07/the-symbolic-link-cannot-be-followed-because-its-type-is-disabled.aspx). J'ai ajusté ta réponse en conséquence. Il récursivement infiniment s'il rencontre un lien hypertexte qui pointe vers un dossier ancêtre; dans mon cas, j'ai juste ignoré les répertoires avec des attributs de 'FileAttributes.ReparsePoint' mais ce n'est probablement pas assez élégant pour une réponse générale – RJFalconer

Questions connexes