ccDirectoryManipulator – Arbitrary Action Given an Arbitrary Condition for Every Subfolder

I was recently re-organizing my music collection and.. Well, I suppose “re-organizing” implies a level of initial organization that my collection has never enjoyed. So..

I was recently organizing my music collection and noticed that it involved a number of repetitive tasks pertaining to folder management. Specifically, I wanted to

  • get rid of folders that only had one file in them
  • get rid of all empty folders
  • Do some mp3 tag reading and make some decisions based on that

The goal is not really the point, but the general task I wanted to completed was enumerate every folder in some base folder, test a condition (e.g. Is directory Empty), and based on the result of the condition execute some action (e.g. delete the directory).

Typically I’d write a script for this (vbscript or maybe batch) but I was recently doing some directory enumeration in C#, so I figured I’d reuse that code.

The result – a shell application to perform an arbitrary task given an arbitrary condition – ccDirectoryManipulator.

How does it work? Well, I don’t know for certain but the code attempts to implement the following:

For each subfolder in the folder specified, call this logic (i.e. recurse). Once the logic returns, call the supplied ‘condition’ delegate with the current subfolder path, and check its return value. If true, call the ‘action’ delegate with the current folder path.

The delegate arrangement is nice, because you can specify different  conditions and subsequent actions. As I mentioned above, some examples can be deleting (action) empty (condition) directories or copying (action) files from directories with less than 5 files (condition).

Or whatever..

Here is the code. Here I’m using anonymous methods for condition and action delegates. Substitute your own code as you see fit!


using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;

namespace cc
{
    class DirectoryManipulator
    {
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine(@"Please specify the starting directory. 
                                    (Use quotes if the directory contains spaces.)");
                Console.WriteLine("USAGE: ccDirectoryManipulator &#91;root directory&#93;\n\n");
                return;
            }
            ConditionalFolderAction(args&#91;0&#93;,
                currentPath =>
                {
                    DirectoryInfo dirInfo = new DirectoryInfo(currentPath);
                    if (dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0)
                        return false;
                    return true;
                },
                currentPath =>
                {
                    //Directory.Delete(currentPath);
                    Console.WriteLine("Will delete " + currentPath);
                    return true;
                });
        }
        /// <summary>
        /// Deletage that will be called to evaluate a conditon
        /// and will also serve as a protype fot the action
        /// </summary>
        /// <param name="path">accepts a full path as an argument</param>
        /// <returns>true of false</returns>
        delegate bool ActionConditionDelegate(string path);
        /// <summary>
        /// Executes the specified action for every sub-folder in the specified 
        /// root directory if the specified condition is true
        /// </summary>
        /// <param name="root">root directory</param>
        /// <param name="condition">a delegate that accepts a path as an argument 
        /// and returns true of false</param>
        /// <param name="action">a delegate that will be called if the condition 
        /// is met with the path</param>
        /// <remarks>Recursive</remarks>
        static void ConditionalFolderAction(string root, ActionConditionDelegate condition, 
            ActionConditionDelegate action)
        {
            // enumerate all directories
            // for each directory, if condition delegate returns true, execute action delegate
            DirectoryInfo rootDirectory = new DirectoryInfo(root);
            foreach (DirectoryInfo currentSubDir in rootDirectory.GetDirectories())
            {
                // first recurse into subdirectories
                ConditionalFolderAction(currentSubDir.FullName, condition, action);
                // then process the current - if directories or files left, do not execute
                if (condition.Invoke(currentSubDir.FullName))
                    action.Invoke(currentSubDir.FullName);
            }
        }
    }
}

Since the operations involve IO, it would be wise to add exception handling. Let me know your thoughts,

Cheers!