How to Delete a File in C# (One File, All Files, by Extension or Age)

Log files pile up fast, and sooner or later your C# app has to clean them up. To delete a file in C#, call File.Delete(path) from System.IO. It removes the file and quietly does nothing if the file isn’t there.

In this tutorial, I’ll build a small log-cleanup console tool step by step and cover every C# delete file task on the way: one file, all files, by extension, all except one, older than N days, keep the newest N, subfolders, read-only and locked files.

The scenario: a Dallas plumbing shop’s point-of-sale app writes a log every day. Each program recreates its own sample files under C:\DotNetDemo\DeleteFilesDemo\Logs, so you can rerun it safely.

What the Finished C# Log Cleaner Deletes

Here’s the end result. The tool scans Logs\Dallas and its month folders, lists the .log files older than 30 days in a dry run, then deletes them. You’ll see readme.txt and October’s log stay put.

VS Code running LogCleaner.cs: a dry run lists the 2026-08 and 2026-09 log files older than 30 days, then the cleanup deletes them
The finished log cleaner: dry run first, then the real delete

I ran each step as a .NET 10 file-based app with dotnet run File.cs, or you can create a console application in Visual Studio Code.

Step 1: Delete One File in C# with File.Delete and File.Exists

This program writes one sample log, checks it with File.Exists, deletes it, then tries two things that trip people up.

string logs = @"C:\DotNetDemo\DeleteFilesDemo\Logs\Single";
Directory.CreateDirectory(logs);
string path = Path.Combine(logs, "dallas-app-2026-10-01.log");
File.WriteAllText(path, "Sample log line");   // recreate the sample file

Console.WriteLine($"Exists before: {File.Exists(path)}");
File.Delete(path);
Console.WriteLine($"Exists after:  {File.Exists(path)}");

File.Delete(path);   // the file is already gone
Console.WriteLine("Second File.Delete on a missing file: no exception");

try
{
    File.Delete(Path.Combine(logs, "NoSuchFolder", "old.log"));
}
catch (DirectoryNotFoundException ex)
{
    Console.WriteLine($"Missing folder: {ex.GetType().Name}");
}

Output:

Exists before: True
Exists after:  False
Second File.Delete on a missing file: no exception
Missing folder: DirectoryNotFoundException
VS Code running DeleteOneFile.cs: File.Exists true before and false after File.Delete, no exception on a missing file, DirectoryNotFoundException for a missing folder
File.Delete removes the file and doesn’t throw when it’s already gone

Checkpoint: the second File.Delete didn’t throw. Microsoft Learn confirms it: a missing file raises no exception, so your File.Exists check is optional.

A missing folder is different. That call threw DirectoryNotFoundException, which is why I call Directory.CreateDirectory first, the same trick from my post on how to create a folder if it does not exist in C#.

Step 2: Delete Files by Extension and Delete All Files Except One

Directory.EnumerateFiles(path, "*.tmp") returns only matching paths, while a plain EnumerateFiles(path) returns every file.

string logs = @"C:\DotNetDemo\DeleteFilesDemo\Logs\Office";
Directory.CreateDirectory(logs);
string[] samples = { "orders.log", "orders.tmp", "upload.tmp", "payroll.csv", "settings.json" };
foreach (string name in samples)
    File.WriteAllText(Path.Combine(logs, name), "sample");   // recreate sample files

// 1. Delete files with one extension
foreach (string file in Directory.EnumerateFiles(logs, "*.tmp"))
{
    File.Delete(file);
    Console.WriteLine($"*.tmp     -> deleted {Path.GetFileName(file)}");
}

// 2. Delete all files except one
string keep = "settings.json";
foreach (string file in Directory.EnumerateFiles(logs))
{
    if (Path.GetFileName(file).Equals(keep, StringComparison.OrdinalIgnoreCase)) continue;
    File.Delete(file);
    Console.WriteLine($"all but 1 -> deleted {Path.GetFileName(file)}");
}

Console.WriteLine("Left in folder: " + string.Join(", ", Directory.EnumerateFiles(logs).Select(Path.GetFileName)));

Output:

*.tmp     -> deleted orders.tmp
*.tmp     -> deleted upload.tmp
all but 1 -> deleted orders.log
all but 1 -> deleted payroll.csv
Left in folder: settings.json
VS Code running DeleteByPattern.cs: the *.tmp files are deleted by search pattern, then every file except settings.json is deleted
Deleting by extension with a search pattern, then deleting all files except one

Checkpoint: only settings.json is left. Remove the if line in the second foreach loop and it deletes all files in the folder.

File.Delete itself doesn’t accept wildcards, so you always enumerate first. On .NET 10, *.xls won’t match .xlsx; that quirk is .NET Framework only.

Step 3: Delete Files Older Than 7 Days and Keep the Newest N

Daily logs need a retention rule. Here I use DirectoryInfo and FileInfo, so each file carries its own LastWriteTimeUtc.

var logs = new DirectoryInfo(@"C:\DotNetDemo\DeleteFilesDemo\Logs\Daily");
logs.Create();
for (int day = 1; day <= 8; day++)              // sample logs 2, 4, 6 ... 16 days old
{
    DateTime stamp = DateTime.UtcNow.AddDays(-2 * day);
    string path = Path.Combine(logs.FullName, $"app-{stamp:yyyy-MM-dd}.log");
    File.WriteAllText(path, "sample");
    File.SetLastWriteTimeUtc(path, stamp);
}

// 1. Delete files older than 7 days
DateTime cutoff = DateTime.UtcNow.AddDays(-7);
foreach (FileInfo file in logs.EnumerateFiles("*.log").Where(f => f.LastWriteTimeUtc < cutoff))
{
    Console.WriteLine($"Older than 7 days: {file.Name}");
    file.Delete();
}

// 2. Keep the newest 2, delete the oldest
foreach (FileInfo file in logs.EnumerateFiles("*.log").OrderByDescending(f => f.LastWriteTimeUtc).Skip(2))
{
    Console.WriteLine($"Beyond newest 2:   {file.Name}");
    file.Delete();
}
Console.WriteLine("Kept: " + string.Join(", ", logs.EnumerateFiles().Select(f => f.Name)));

Output:

Older than 7 days: app-2026-10-09.log
Older than 7 days: app-2026-10-11.log
Older than 7 days: app-2026-10-13.log
Older than 7 days: app-2026-10-15.log
Older than 7 days: app-2026-10-17.log
Beyond newest 2:   app-2026-10-19.log
Kept: app-2026-10-21.log, app-2026-10-23.log
VS Code running DeleteOldFiles.cs: log files older than 7 days are deleted by LastWriteTimeUtc, then only the newest 2 logs are kept
Deleting files older than 7 days, then deleting the oldest files beyond the newest 2

Checkpoint: five logs failed the 7-day test, and the 6-day-old one went in the keep-newest pass. Comparing UTC to UTC means daylight saving can’t shift the cutoff.

OrderByDescending(...).Skip(2) sorts newest first and skips the two you keep. If ordering is new to you, see how to sort an array in C#.

Why Can’t C# Delete a Read-Only or Locked File?

A read-only file throws UnauthorizedAccessException, and a file another process holds open throws IOException. Here’s both, plus the fix.

string logs = @"C:\DotNetDemo\DeleteFilesDemo\Logs\Locked";
Directory.CreateDirectory(logs);
string readOnly = Path.Combine(logs, "archive-2026-09.log");
string inUse = Path.Combine(logs, "today.log");
foreach (string f in new[] { readOnly, inUse })
{
    if (File.Exists(f)) File.SetAttributes(f, FileAttributes.Normal);
    File.WriteAllText(f, "sample");
}
File.SetAttributes(readOnly, File.GetAttributes(readOnly) | FileAttributes.ReadOnly);

try { File.Delete(readOnly); }
catch (UnauthorizedAccessException ex) { Console.WriteLine($"Read-only: {ex.GetType().Name}"); }

File.SetAttributes(readOnly, FileAttributes.Normal);
File.Delete(readOnly);
Console.WriteLine($"Read-only deleted after clearing the flag: {!File.Exists(readOnly)}");

using (var stream = new FileStream(inUse, FileMode.Open, FileAccess.Read, FileShare.None))
{
    try { File.Delete(inUse); }
    catch (IOException ex) { Console.WriteLine($"Locked: {ex.Message}"); }
}
File.Delete(inUse);   // the using block closed the handle
Console.WriteLine($"Locked file deleted after the handle closed: {!File.Exists(inUse)}");

Output:

Read-only: UnauthorizedAccessException
Read-only deleted after clearing the flag: True
Locked: The process cannot access the file 'C:\DotNetDemo\DeleteFilesDemo\Logs\Locked\today.log' because it is being used by another process.
Locked file deleted after the handle closed: True
VS Code running ReadOnlyAndLocked.cs: UnauthorizedAccessException for a read-only file, IOException for a file opened with FileShare.None, both deleted after the fix
A read-only file and a locked file: the exceptions and how to delete them

Checkpoint: clearing the flag with File.SetAttributes(path, FileAttributes.Normal) made the delete work. FileAttributes is a flags enum, which I explain in my post on enums in C#.

The locked file was deletable as soon as the using block closed the stream. In your own code, always dispose streams, as covered in Dispose and Finalize in C#.

Step 4: Delete Files Recursively with a Dry Run First

Here’s the final tool from the first screenshot. SearchOption.AllDirectories walks every subfolder, and a dryRun flag prints what would go before anything is deleted.

string root = @"C:\DotNetDemo\DeleteFilesDemo\Logs\Dallas";
foreach (int month in new[] { 8, 9, 10 })       // recreate one sample log per month folder
{
    string dir = Directory.CreateDirectory(Path.Combine(root, $"2026-{month:00}")).FullName;
    string file = Path.Combine(dir, $"pos-2026-{month:00}-15.log");
    File.WriteAllText(file, "sample");
    File.SetLastWriteTimeUtc(file, new DateTime(2026, month, 15, 0, 0, 0, DateTimeKind.Utc));
}
File.WriteAllText(Path.Combine(root, "readme.txt"), "keep me");

Clean(dryRun: true);
Clean(dryRun: false);

void Clean(bool dryRun)
{
    DateTime cutoff = DateTime.UtcNow.AddDays(-30);
    int count = 0;
    foreach (string file in Directory.EnumerateFiles(root, "*.log", SearchOption.AllDirectories))
    {
        if (File.GetLastWriteTimeUtc(file) >= cutoff) continue;
        try
        {
            if (!dryRun) File.Delete(file);
            count++;
            Console.WriteLine($"{(dryRun ? "Would delete" : "Deleted")}: {Path.GetRelativePath(root, file)}");
        }
        catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
        {
            Console.WriteLine($"Skipped {Path.GetFileName(file)}: {ex.Message}");
        }
    }
    Console.WriteLine($"{(dryRun ? "Dry run" : "Cleanup")} finished: {count} file(s)");
}

Output:

Would delete: 2026-08\pos-2026-08-15.log
Would delete: 2026-09\pos-2026-09-15.log
Dry run finished: 2 file(s)
Deleted: 2026-08\pos-2026-08-15.log
Deleted: 2026-09\pos-2026-09-15.log
Cleanup finished: 2 file(s)

Checkpoint: the dry run and the cleanup list the same two files. The catch filter skips locked or read-only files instead of stopping the job; my try-catch guide for C# explains exception filters.

The empty 2026-08 and 2026-09 folders stay behind, because File.Delete never removes folders. Use Directory.Delete for that, as shown in my post on how to delete a folder in C#.

Which C# Delete File Method Should You Pick?

TaskUse this
Delete one fileFile.Delete(path)
Delete by extensionDirectory.EnumerateFiles(dir, "*.tmp") + File.Delete
Older than N daysFileInfo.LastWriteTimeUtc < DateTime.UtcNow.AddDays(-n)
Keep the newest NOrderByDescending(f => f.LastWriteTimeUtc).Skip(n)
Read-only fileClear FileAttributes.ReadOnly first

My rule: run every bulk delete as a dry run first, and point it only at a folder your app owns. Deleted files don’t go to the Recycle Bin.

Want to keep building on this tool? These posts pair well with it:

Frequently Asked Questions

Does File.Delete throw an exception if the file doesn’t exist?

No, a missing file is fine. It does throw DirectoryNotFoundException when the folder in the path doesn’t exist. See the File.Delete documentation on Microsoft Learn.

How do I delete all files in a folder in C#?

Loop over Directory.EnumerateFiles(folder) and call File.Delete on each path. Add SearchOption.AllDirectories to include subfolders.

Can File.Delete use wildcards like *.txt?

No, the path can’t contain wildcards. Pass the pattern to Directory.EnumerateFiles(folder, "*.txt") and delete each result. The EnumerateFiles reference lists the pattern rules.

How do I delete files older than 30 days in C#?

Delete each file whose LastWriteTimeUtc is earlier than DateTime.UtcNow.AddDays(-30). Use CreationTimeUtc to go by creation date.

What’s the difference between File.Delete and FileInfo.Delete?

They do the same job. File.Delete takes a path; FileInfo.Delete works on a FileInfo you already have, which is handy when you filter by date or size.