2010-05-11 3 views
27

J'ai pour tâche de nettoyer un grand nombre de répertoires. Je veux commencer à un répertoire et supprimer tous les sous-répertoires (peu importe la profondeur) qui ne contiennent aucun fichier (les fichiers ne seront jamais supprimés, seulement les répertoires). Le répertoire de départ sera alors supprimé s'il ne contient aucun fichier ou sous-répertoire. J'espérais que quelqu'un pourrait me diriger vers un code existant plutôt que de réinventer la roue. Je vais le faire en utilisant C#.C# Supprimer tous les sous-répertoires vides

+2

Pourquoi utiliser powershell ...? –

Répondre

65

Utilisation du code C#.

static void Main(string[] args) 
{ 
    processDirectory(@"c:\temp"); 
} 

private static void processDirectory(string startLocation) 
{ 
    foreach (var directory in Directory.GetDirectories(startLocation)) 
    { 
     processDirectory(directory); 
     if (Directory.GetFiles(directory).Length == 0 && 
      Directory.GetDirectories(directory).Length == 0) 
     { 
      Directory.Delete(directory, false); 
     } 
    } 
} 
+1

Merci beaucoup, exactement ce que je cherchais. – Jay

+14

Une façon plus rapide d'écrire votre déclaration 'if' pourrait être' if (Directory.GetFileSystemEntries (répertoire) .Length == 0) ' –

+6

(! Directory.EnumerateFileSystemEntries (répertoire) .Any()) est mieux – prime23

4

A partir de là, Powershell script to remove empty directories:

$items = Get-ChildItem -Recurse 

foreach($item in $items) 
{ 
     if($item.PSIsContainer) 
     { 
      $subitems = Get-ChildItem -Recurse -Path $item.FullName 
      if($subitems -eq $null) 
      { 
        "Remove item: " + $item.FullName 
        Remove-Item $item.FullName 
      } 
      $subitems = $null 
     } 
} 

Remarque: utilisation à vos propres risques!

+0

Powershell n'est pas une option, désolé. Merci quand même. – Jay

32

Si vous pouvez cibler le .NET 4.0, vous pouvez utiliser les nouvelles méthodes de la classe Directory d'énumérer les répertoires afin de ne pas payer une pénalité de performance dans la liste tous les fichiers dans un répertoire où vous voulez juste savoir s'il y en a au moins un.

Les méthodes sont les suivantes:

  • Directory.EnumerateDirectories
  • Directory.EnumerateFiles
  • Directory.EnumerateFileSystemEntries

Une implémentation possible en utilisant récursion:

static void Main(string[] args) 
{ 
    DeleteEmptyDirs("Start"); 
} 

static void DeleteEmptyDirs(string dir) 
{ 
    if (String.IsNullOrEmpty(dir)) 
     throw new ArgumentException(
      "Starting directory is a null reference or an empty string", 
      "dir"); 

    try 
    { 
     foreach (var d in Directory.EnumerateDirectories(dir)) 
     { 
      DeleteEmptyDirs(d); 
     } 

     var entries = Directory.EnumerateFileSystemEntries(dir); 

     if (!entries.Any()) 
     { 
      try 
      { 
       Directory.Delete(dir); 
      } 
      catch (UnauthorizedAccessException) { } 
      catch (DirectoryNotFoundException) { } 
     } 
    } 
    catch (UnauthorizedAccessException) { } 
} 

Vous mentionnez également que l'arborescence de répertoires peut être très profonde, il est donc possible que vous obteniez des exceptions si le chemin que vous recherchez est trop long.

+0

Merci, mais malheureusement, nous n'utilisons pas .Net 4.0 .Je souhaite que nous pourrions comme j'ai environ 20.000 dossiers t o processus. – Jay

+2

Bonne réponse. Au lieu de 'if (String.IsNullOrEmpty (entries.FirstOrDefault()))', vous pouvez également utiliser 'if (! Entries.Any())', ce qui est un peu plus propre à mon humble avis. –

+1

@Danko Durbić, tout à fait d'accord avec vous, je n'ai pas remarqué la surcharge sans paramètres et me demandais déjà pourquoi 'Enumerable' n'avait pas quelque chose à vérifier rapidement pour un' IEnumerable' vide. Merci, j'ai mis à jour la réponse. –

3

Si vous comptez sur DirectoryInfo.Delete que la suppression des répertoires vides, vous pouvez écrire une méthode d'extension succincte

public static void DeleteEmptyDirs(this DirectoryInfo dir) 
{ 
    foreach (DirectoryInfo d in dir.GetDirectories()) 
     d.DeleteEmptyDirs(); 

    try { dir.Delete(); } 
    catch (IOException) {} 
    catch (UnauthorizedAccessException) {} 
} 

Utilisation:

static void Main() 
{ 
    new DirectoryInfo(@"C:\temp").DeleteEmptyDirs(); 
} 
0
//Recursive scan of empty dirs. See example output bottom 

string startDir = @"d:\root"; 

void Scan(string dir, bool stepBack) 
    { 
     //directory not empty 
     if (Directory.GetFileSystemEntries(dir).Length > 0) 
     { 
      if (!stepBack) 
      { 
       foreach (string subdir in Directory.GetDirectories(dir)) 
        Scan(subdir, false); 
      } 
     } 
     //directory empty so delete it. 
     else 
     { 
      Directory.Delete(dir); 
      string prevDir = dir.Substring(0, dir.LastIndexOf("\\")); 
      if (startDir.Length <= prevDir.Length) 
       Scan(prevDir, true); 
     } 
    } 
//call like this 
Scan(startDir, false); 

/*EXAMPLE outputof d:\root with empty subfolders and one filled with files 
    Scanning d:\root 
    Scanning d:\root\folder1 (not empty) 
    Scanning d:\root\folder1\folder1sub1 (not empty) 
    Scanning d:\root\folder1\folder1sub1\folder2sub2 (deleted!) 
    Scanning d:\root\folder1\folder1sub1 (deleted!) 
    Scanning d:\root\folder1 (deleted) 
    Scanning d:\root (not empty) 
    Scanning d:\root\folder2 (not empty) 
    Scanning d:\root\folder2\folder2sub1 (deleted) 
    Scanning d:\root\folder2 (not empty) 
    Scanning d:\root\folder2\notempty (not empty) */ 
+1

Cet algoritm supprimera tous les répertoires vides dans un répertoire de démarrage donné. Lorsqu'un répertoire est supprimé, le répertoire précédent sera ré-analysé car il est possible que le répertoire précédent soit maintenant vide. – Jamil

+0

Ce n'est pas nécessaire. Vous devriez penser aux avantages de la récursivité et peut-être déboguer lentement à travers les réponses déjà fournies. – Oliver

+0

Vrai, mais qu'est-ce qui n'est pas récursif à propos de ça? J'étais un peu trop rapide et j'ai donc fait une petite correction. Voir l'exemple de sortie sur ce que fait ce code. Je pense que c'est efficace. – Jamil

6

Exécution du test sur C: \ Windows 1000 fois sur les 3 méthodes mentionnées jusqu'à présent cédé ceci:

GetFiles+GetDirectories:630ms 
GetFileSystemEntries:295ms 
EnumerateFileSystemEntries.Any:71ms 

Courir sur un dossier vide a abouti à ce (1000 fois): encore

GetFiles+GetDirectories:131ms 
GetFileSystemEntries:66ms 
EnumerateFileSystemEntries.Any:64ms 

Alors EnumerateFileSystemEntries est de loin le meilleur ensemble lorsque vous vérifiez les dossiers vides.

+0

131ms dans un dossier vide !? – Patrick

+1

1000 fois sur un dossier vide. – Wolf5

+0

Ahh oui. Slow brain day. C'était un lundi :) – Patrick

1
private static void deleteEmptySubFolders(string ffd, bool deleteIfFileSizeZero=false) 
{ 
    DirectoryInfo di = new DirectoryInfo(ffd); 
    foreach (DirectoryInfo diSon in di.GetDirectories("*", SearchOption.TopDirectoryOnly)) 
    { 
     FileInfo[] fis = diSon.GetFiles("*.*", SearchOption.AllDirectories); 
     if (fis == null || fis.Length < 1) 
     { 
      diSon.Delete(true); 
     } 
     else 
     { 
      if (deleteIfFileSizeZero) 
      { 
       long total = 0; 
       foreach (FileInfo fi in fis) 
       { 
        total = total + fi.Length; 
        if (total > 0) 
        { 
         break; 
        } 
       } 

       if (total == 0) 
       { 
        diSon.Delete(true); 
        continue; 
       } 
      } 

      deleteEmptySubFolders(diSon.FullName, deleteIfFileSizeZero); 
     } 
    } 
} 
2

est ici une version qui profite de l'exécution en parallèle pour le faire plus rapidement dans certains cas:

public static void DeleteEmptySubdirectories(string parentDirectory){ 
    System.Threading.Tasks.Parallel.ForEach(System.IO.Directory.GetDirectories(parentDirectory), directory => { 
    DeleteEmptySubdirectories(directory); 
    if(!System.IO.Directory.EnumerateFileSystemEntries(directory).Any()) System.IO.Directory.Delete(directory, false); 
    }); 
} 

est ici le même code en mode mono-thread:

public static void DeleteEmptySubdirectoriesSingleThread(string parentDirectory){ 
    foreach(string directory in System.IO.Directory.GetDirectories(parentDirectory)){ 
    DeleteEmptySubdirectories(directory); 
    if(!System.IO.Directory.EnumerateFileSystemEntries(directory).Any()) System.IO.Directory.Delete(directory, false); 
    } 
} 

... et voici quelques exemples de code que vous pourriez utiliser pour tester les résultats dans votre scénario:

var stopWatch = new System.Diagnostics.Stopwatch(); 
for(int i = 0; i < 100; i++) { 
    stopWatch.Restart(); 
    DeleteEmptySubdirectories(rootPath); 
    stopWatch.Stop(); 
    StatusOutputStream.WriteLine("Parallel: "+stopWatch.ElapsedMilliseconds); 
    stopWatch.Restart(); 
    DeleteEmptySubdirectoriesSingleThread(rootPath); 
    stopWatch.Stop(); 
    StatusOutputStream.WriteLine("Single: "+stopWatch.ElapsedMilliseconds); 
} 

... et voici quelques résultats de ma machine pour un répertoire qui se trouve sur un partage de fichiers sur un réseau étendu. Ce partage a actuellement seulement 16 sous-dossiers et 2277 fichiers.

Parallel: 1479 
Single: 4724 
Parallel: 1691 
Single: 5603 
Parallel: 1540 
Single: 4959 
Parallel: 1592 
Single: 4792 
Parallel: 1671 
Single: 4849 
Parallel: 1485 
Single: 4389 
+0

Très utile! merci, Bill – BillW

Questions connexes