Clean Visual Studio workspace with PowerShell

Written on July 31, 2014

When working in Visual Studio all sorts of temporary files get created in your workspace. From user specific *.suo, *.user and *.docstates files, to the build results in the bin/ and obj/ directories up to extension specific files for e.g. ReSharper. Just to name a few.

To get the situation under control again two things are necessary:

  • A good .gitignore file (or the equivalent for the source control of your choice)
  • A script to wipe all temporary and unwanted files for a truly clean repository

Running Visual Studio’s Clean Solution command doesn’t do the trick. It’s not configurable, only removes build artefacts and isn’t even reliable in doing that always for good.

So, if you run into an odd issue and just want to compile a truly clean solution from scratch, Clean Solution just isn’t an option.

One of my all-time lifesaver scripts that I use quite frequently is this little PowerShell gem:

# Define files and directories to delete
$include = @("*.suo","*.user","*.cache","*.docstates","bin","obj","build")

# Define files and directories to exclude
$exclude = @()

$items = Get-ChildItem . -recurse -force -include $include -exclude $exclude

if ($items) {
    foreach ($item in $items) {
        Remove-Item $item.FullName -Force -Recurse -ErrorAction SilentlyContinue
        Write-Host "Deleted" $item.FullName

Write-Host "Press any key to continue . . ."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

Placed in the root of your repository as clean.ps1 you can execute it whenever you feel dirty (right mouse click and Run with PowerShell). Note that in order to run the script you might need to change your PowerShell script execution policy.

Let’s have a quick look of what it does, shall we?

The variable $include holds an array of path elements or patterns to delete, such as *.suo. Wildcards are permitted.

The variable $exclude holds an array of path elements or patterns to omit within the defined scope for deletion. It’s empty in the example above but it could look like this:

$exclude = @("ConnectionStrings.config.user")

The Cmdlet Get-ChildItem gets all the items and child items for the specified locations. Note that we’re passing the $include and $exclude arrays as parameters.

Get-ChildItem . -recurse -force -include $include -exclude $exclude

Next, it loops over all of these items and deletes them one by one using Remove-Item.

Remove-Item $item.FullName -Force -Recurse -ErrorAction SilentlyContinue

The last line is not necessary and really just a preference of mine - how I usually execute it - so that the script pauses until you press any key on the keyboard.

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

You can find the code on GitHub.

Martin Buberl

Purveyor of Internet duct tape.
If you'd like to get in touch, feel free to shout @martinbuberl.