More PowerShell – a Cmdlet

This past weekend I spent some time reading up on and writing my first PowerShell cmdlet.   The cmdlet is an easy one, but replaces a PS function I have copy/pasted into several scripts here and there to handle cleaning out directories of old backups or other types of files.  One scenario is my RadioShark which I have setup to make daily radio recordings.  The software doesn’t have any settings for how long to keep the files, so up until now I have just been manually deleting a couple dozen at a time every month or so. 

Enter Remove-OldItems, named after the built-in Remove-Item.

Full Syntax: Remove-OldItems c:temp -pattern *.mp3 -KeepDays 7 -Leave 2 -Confirm -WhatIf

That will remove all mp3 files in c:temp which are older than 7 days.  The Leave parameter is for when I use it for dealing with backup files and is a safeguard so that even if all of the files fall out of the date range, they won’t all be deleted. 

I have put together 32-bit and 64-bit installers, feel free to download them.  If you run it on a 64-bit system, the install will register with both the 32 and 64bit PowerShells. I can make the code available if anyone’s interested.

After installing the .msi, you can confirm the new Snapin is on the system by running get-pssnapin -registered. You should see EjiSnapin listed.After Install

Now the Snapin containing the cmdlet(s) is there, but not loaded into the current PowerShell session.  Since I’m going to be running this from a Scheduled Task, I don’t want to have to explicitly run Add-PSSnapin EjiSnapin every time I want to use it.  One quick way around this is to add that command to the system-wide PowerShell Profile located at $pshomeprofile.ps1.  That’s easy enough, in an elevated PS prompt, just run notepad $pshome/profile.ps1 and add a single line Add-PSSnapin EjiSnapin and save the file.  Now every PS session will have the cmdlet ready to go.

Here’s the final result, a scheduled task with a simple command being run to clear out old files in my RadioShark directory.

task

Overall it was fairly easy, the most difficult part came with getting the installers to work correctly between 32 and 64-bit installations.  Future enhancements, if I come into a situation where I need it, may be to add processing from the pipeline so a collection of files to be deleted could be passed to the cmdlet instead of a directory path.  Might be useful, might not.

Here’s C# for doing the actual deletions.


    DirectoryInfo rootDir = new DirectoryInfo(Path);

    // anything older than Today minus KeepDays may be deleted
    DateTime protectionDate = DateTime.UtcNow.AddDays(-KeepDays);

    List candidates = rootDir.GetFiles(string.IsNullOrEmpty(Pattern) ? "*.*" : Pattern, SearchOption.TopDirectoryOnly)
        .OrderBy(f => f.LastWriteTimeUtc).ToList();

    if (Leave > 0)
    {
        // pop the last Leave files off the end (the most recent)
        candidates.RemoveRange(candidates.Count - Leave, Leave);
    }

    // now only keep those that are old enough
    candidates.RemoveAll(f => f.LastWriteTimeUtc >= protectionDate);

    // cycle and delete
    candidates.ForEach(f =>
        {
            if (ShouldProcess(f.FullName, "delete"))
            {
                try
                {
                    f.Delete();
                }
                catch (UnauthorizedAccessException ex)
                {
                    WriteWarning(String.Format("Unable to delete '{0}', UnauthorizedAccess", f.FullName));

                    // IOException can also occur, but I want that to be a termanating exception
                }

            }
            else
            {
                    // Nothing to do, ShouldProcess provided any errors/warnings/etc.
            }
        });

In the process of writing this I learned that PowerShell V2 allows you to script cmdlets.  So this could have been greatly simplified (mostly on the deployment side), but that wouldn’t have been nearly as interesting, right? 

That’s all folks. Thanks for reading.