Click here to Skip to main content
15,068,646 members
Articles / Programming Languages / C#
Posted 12 Feb 2005


48 bookmarked

DeleteOld : Console Application to Delete Files by Age

Rate me:
Please Sign up or sign in to vote.
4.44/5 (14 votes)
17 Oct 2008CPOL3 min read
A C# console application for deleting files based on age.



DeleteOld is a C# console application to delete 'old' files. The application uses a series of command-line switches to control the execution. I've taken ideas from similar apps, so certainly can't claim to have invented this concept!

I found that the (free) applications out there, that do this, either don't work fully or were a bit 'clunky' — so I decided to 'reinvent the wheel' and write my own implementation in C#.

Updates (Oct 2008)

This version now includes the following new functionality (mostly from comments on the article)

  • Allow files to be deleted 'newer' than a certain age as well as 'older'
  • Allow files to be deleted based on an absolute date (overriding the timeframe arguments)
  • Allow the 'root' path to be preserved if 'remove empty folders' is selected and the path specified is empty.
  • Fixed a bug in Arguments parsing regex as noted by dudik
  • Changed output datetime format to 'full' rather than specific dd/mm/yyyy as noted by

Using the Code

The biggest challenge originally was command line parameters. I used a slightly modified version of Richard Lopes's Arguments class to achieve what I wanted with a variety of mandatory and optional parameters — some are name/value pairs and others are simple switches.

The rest is fairly straightforward (in C# terms) and should be relatively easy to follow. Once the input parameters have been validated, the main processing is a fairly simple recursive loop around finding and deleting files in a directory tree.

It's important to note that we need to set a consistent time for file time comparison purposes — so the following code gets a 'benchmark date' (based on the requested timeframe) before processing begins.

/// <summary>
/// Sets the delete time benchmark.
/// </summary>
private void SetDeleteTimeBenchmark()
	if (age != int.MaxValue)
		if (timeFrame == AgeIncrement.Second)
			deleteBaselineDate = runTime.AddSeconds(-age);
		if (timeFrame == AgeIncrement.Minute)
			deleteBaselineDate = runTime.AddMinutes(-age);
		if (timeFrame == AgeIncrement.Hour)
			deleteBaselineDate = runTime.AddHours(-age);
		if (timeFrame == AgeIncrement.Day)
			deleteBaselineDate = runTime.AddDays(-age);
		if (timeFrame == AgeIncrement.Month)
			deleteBaselineDate = runTime.AddMonths(-age);
		if (timeFrame == AgeIncrement.Year)
			deleteBaselineDate = runTime.AddYears(-age);
	//If a date has been provided then this takes precedence
	if (absoluteDate != null && absoluteDate != DateTime.MinValue)
		deleteBaselineDate = absoluteDate;

We can then process our actual command and take all of our command-line options into account as we go:

/// <summary>
/// Deletes the files (potentially recursively) from given folder.
/// </summary>
private int DeleteFilesFromFolder(DirectoryInfo folder, bool simulateOnly)
	foreach(FileInfo file in folder.GetFiles(filter))
		if (!deleteNewer ? file.LastWriteTime <
                      deleteBaselineDate : file.LastWriteTime > deleteBaselineDate)
				if (! quietMode)
					outputStream.WriteLine("Deleting {0}",
				if (! simulateOnly)
			catch (Exception ex)
				if (! quietMode)
                                              "Could not delete file {0} : Reason {1}",
                                              file.FullName, ex.Message);

	if (recurseSubFolders)
		foreach(DirectoryInfo subFolder in folder.GetDirectories())
			DeleteFilesFromFolder(subFolder, simulateOnly);

	if (removeEmptyFolders)
		if (folder.FullName == path && dontRemoveEmptyRootFolder)
			outputStream.WriteLine("Leaving empty root folder {0}",
		else if (folder.GetFiles().Length == 0 &&
                      folder.GetDirectories().Length == 0)
			if (! quietMode)
				outputStream.WriteLine("Deleting empty folder {0}",

				if (! simulateOnly)
			catch (Exception ex)
				if (! quietMode)
                                               "Could not delete folder {0} : Reason {1}",
                                               folder.FullName, ex.Message);

	return 0;

You'll notice that there is an NUnit tests class bundled into the project, so if you want to build the project and don't have NUnit 2.2 installed, you'll need to exclude DeleteOldTests.cs from the project and remove the reference to NUnit.

Points of Interest

Unit Tests

One of the interesting obstacles was how to usefully unit test such an application. Console apps are a bit troublesome in that all the code is typically written into the .exe (to keep deployment light), and a separate .NET assembly (e.g. Unit Tests) can't reference a .exe assembly. I could have of course used a post-build event to make a copy as DeleteOld.dll and reference that from a tests assembly but I felt, that was a bit too much of a compromise.

I typically wanted to test output from the command based on the input parameters and the files I was looking to delete. This meant that the easiest way, was to provide programmatic access to override the output streams used at runtime. The default constructor uses standard Console streams:

public DeleteOld(Arguments args)
	outputStream = new StreamWriter(Console.OpenStandardOutput());
	outputStream.AutoFlush = true;
	errorOutputStream = new StreamWriter(Console.OpenStandardError());
	errorOutputStream.AutoFlush = true;


The unit tests use the other constructor to pass in a MemoryStream.

public DeleteOld(Arguments args, Stream outputStream, Stream errorOutputStream)
	this.outputStream = new StreamWriter(outputStream);
	this.outputStream.AutoFlush = true;
	this.errorOutputStream = new StreamWriter(errorOutputStream);
	this.errorOutputStream.AutoFlush = true;



This technique effectively enables the tests to do the following in order to setup and check the output of the command:

public void DirectoryNotExist()

	Environment.CurrentDirectory = Path.Combine(Path.GetTempPath(), "DeleteOldtests");
	string[] args = GetInvalidArguments2();
	MemoryStream ms = new MemoryStream();
	Arguments arguments = new Arguments(args);

	DeleteOld DeleteOld = new DeleteOld(arguments, ms, ms);
	string output = System.Text.Encoding.Default.GetString(ms.ToArray());

	//Ensure output is correct
             @"Directory does not exist : c:\vfchtugierujkjhsdfsdf\ffklksdfs\jlkjkvvdd1"));


There's also a public Validated property in the class to assist with unit testing.

I think I had more fun (and spent more time) with the unit test side of things, than the actual functionality, which was pretty straightforward once you've parsed all of the arguments.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

CodeBureau - Matt Simner
Software Developer (Senior) Codebureau
Australia Australia
Started with COBOL/CICS/DB2 - ended up with C#/ASP.NET. I'm a half-geek (or so my wife tells me!)

Comments and Discussions

QuestionMultiple File filter Pin
Murugavel Sadagopan9-Jul-15 1:54
MemberMurugavel Sadagopan9-Jul-15 1:54 
QuestionHow to test this application Pin
Jiewei Xu22-Jun-13 7:06
MemberJiewei Xu22-Jun-13 7:06 
QuestionDeep FilePaths Pin
jasondemont29-Apr-13 8:22
Memberjasondemont29-Apr-13 8:22 
AnswerRe: Deep FilePaths Pin
jasondemont29-Apr-13 8:27
Memberjasondemont29-Apr-13 8:27 
GeneralRe: Deep FilePaths Pin
jasondemont29-Apr-13 8:28
Memberjasondemont29-Apr-13 8:28 
GeneralMissing reference - nunit.framework Pin
Mark Vinogradov11-Apr-11 11:20
MemberMark Vinogradov11-Apr-11 11:20 
GeneralRe: Missing reference - nunit.framework Pin
CodeBureau - Matt Simner11-Apr-11 12:09
MemberCodeBureau - Matt Simner11-Apr-11 12:09 
GeneralMy vote of 5 Pin
id_cegep14-Jan-11 13:08
Memberid_cegep14-Jan-11 13:08 
GeneralGreat app, nice code, good article Pin
Michael Loughlin29-Jan-09 1:36
MemberMichael Loughlin29-Jan-09 1:36 
QuestionAwesome script Pin
greg cummings29-Sep-08 2:27
Membergreg cummings29-Sep-08 2:27 
AnswerRe: Awesome script Pin
CodeBureau - Matt Simner12-Oct-08 14:05
MemberCodeBureau - Matt Simner12-Oct-08 14:05 
AnswerRe: Awesome script Pin
CodeBureau - Matt Simner12-Oct-08 14:51
MemberCodeBureau - Matt Simner12-Oct-08 14:51 
GeneralCool! Pin
Daniel Carvalho Liedke10-Sep-07 9:07
MemberDaniel Carvalho Liedke10-Sep-07 9:07 
Generaldate format Pin 11:18 11:18 
GeneralFeature Request = Except Certain Files Pin
cgoodman19766-Mar-07 11:42
Membercgoodman19766-Mar-07 11:42 
GeneralThank you! Pin
mkitchin28-Nov-06 13:16
Membermkitchin28-Nov-06 13:16 
Generalhour argument in TimeFrame Pin
dudik17-Apr-06 3:16
Memberdudik17-Apr-06 3:16 
GeneralWarning extension bug Pin
nate chadwick9-Aug-05 10:20
Membernate chadwick9-Aug-05 10:20 
GeneralRe: Warning extension bug Pin
CodeBureau - Matt Simner21-Aug-05 2:33
MemberCodeBureau - Matt Simner21-Aug-05 2:33 
Generalswitch Pin
darrylobrien21-Jul-05 9:11
Memberdarrylobrien21-Jul-05 9:11 
GeneralRe: switch Pin
CodeBureau - Matt Simner25-Jul-05 1:21
MemberCodeBureau - Matt Simner25-Jul-05 1:21 
Questioncan u del files newr then and older then? Pin
jbu2018-Jun-05 0:49
Memberjbu2018-Jun-05 0:49 
GeneralNeed to reformat the article Pin
Judah Gabriel Himango1-Mar-05 11:39
sponsorJudah Gabriel Himango1-Mar-05 11:39 
GeneralUnable to download files Pin
AlexanderAC17-Feb-05 10:48
MemberAlexanderAC17-Feb-05 10:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.