web-dev-qa-db-fra.com

C # Supprimer tous les sous-répertoires vides

J'ai la tâche de nettoyer un grand nombre de répertoires. Je veux commencer dans un répertoire et supprimer tous les sous-répertoires (quelle que soit la profondeur) qui ne contiennent aucun fichier (les fichiers ne seront jamais supprimés, uniquement 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 pour cela plutôt que d'avoir à réinventer la roue. Je vais le faire en utilisant C #.

45
Jay

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

Si vous pouvez cibler le .NET 4.0, vous pouvez utiliser les nouvelles méthodes de la classe Directory pour énumérer les répertoires afin de ne pas payer de pénalité en termes de performances en répertoriant tous les fichiers d'un répertoire lorsque vous voulez simplement savoir s'il existe est au moins un.

Les méthodes sont les suivantes:

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

Une implémentation possible utilisant la récursivité:

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 sondez est trop long.

39
João Angelo

L'exécution du test sur C:\Windows 1000 fois sur les 3 méthodes mentionnées jusqu'à présent a donné ceci:

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

L'exécuter sur un dossier vide a produit ceci (1000 fois encore):

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

EnumerateFileSystemEntries est donc de loin le meilleur dans l'ensemble lorsque vous recherchez des dossiers vides.

8
Wolf5

À partir d'ici, script Powershell pour supprimer les répertoires vides :

$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 : utilisez à vos risques et périls!

4
Mitch Wheat

Si vous comptez sur DirectoryInfo.Delete en supprimant uniquement les 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) {}
}

Usage:

static void Main()
{
    new DirectoryInfo(@"C:\temp").DeleteEmptyDirs();
}
3
Neil

Voici une version qui tire parti de l'exécution 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);
  });   
}

Voici le même code en mode simple 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 un exemple 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 ne compte actuellement que 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
2
scradam
    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);
        }
    }
}
1
Mavei
    foreach (var folder in Directory.GetDirectories(myDir, "*", System.IO.SearchOption.AllDirectories))
    {
        {
            try
            {
                if (Directory.GetFiles(folder, "*", System.IO.SearchOption.AllDirectories).Length == 0)
                    Directory.Delete(folder, true);
            }
            catch { }
        }
    }
0
Sam
//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) */
0
Jamil