My CodeShare

So, for my CodeShare Week submission I’m going to show a PowerShell script that I recently put together at the last CINNUG BitSlingers event.  One of the attendees asked if there was a way in PowerShell to delete all the files in a given folder, recursively, but with some exclusions.  The specific scenario was to delete all the files in a folder that served as a web root but not remove the web.config and other possible supporting files that were specific to the environment.  Then they could copy the changed bits in from another location after starting with a clean slate.

Here is what I came up with:

get-childitem | foreach-object {remove-item $_ -Recurse -exclude web.config, dontDelete.txt}

What this script does is it uses get-childitem to produce a list of all the files in the current directory and pipes that to a foreach-object cmdlet.  For each item in the list it performs a remove-item recursively (to get rid of the directories), but excludes files named web.config and dontDelete.txt.

While I was reviewing this to include in my blog post here I thought of a couple of ways to improve it.  First of all, this requires that I run it in the directory I’m wanting to remove stuff from.  Also, this only looks at the top level of items in the root folder, which means if say the dontDelete.txt file existed nested a few folders down it would still get deleted.  So, I’ve decided to revamp my script to solve these issues.

$items = get-childitem "C:\temp\testdir" -recurse
$items = $items[($items.length -1) .. 0]
$items | foreach-object {if (($_.psIsContainer -eq $true -and $_.length -eq 0) -or $_.psIsContainer -eq $false) {remove-item $_.FullName -exclude web.config, dontdelete.txt}}

Okay, so this time I’m getting the list of files at a specific location, not just where the script was run.  Notice here that I’m adding the recurse option at this level this time so that the $item variable now holds a list of all the folders and files from C:\temp\testdir down.  I then reverse the order of the list.  I do that because I want to walk backwards up the tree from the lowest directories up to the root (you’ll see why in a second).  You could have also used the Reverse function on the static .NET Array class to accomplish the reversal.  Like this: $items = [Array]::Reverse($items)

The next line takes the contents of the now reversed list and iterates through each one (using the foreach-object).  If the item is a directory ($_psiIsContainer is true) and the directory is empty, or if the item is just a file, then remove the item but exclude web.config and dontdelete.txt.  I needed to walk the list from the lowest level up so that I deleted the files the furthest in the tree first.  Otherwise I either would have been told I couldn’t remove a directory because it had files in it, or, if the override was on, it would have possibly removed the folders that had files that I wanted excluded and taken those supposedly excluded files with it.

You could adapt this script to delete specific files rather than excluding them, or even extend it to be a function in PowerShell that takes a directory and string list of files to exclude.

I hope you get some use out of this in some manner.  Even if you don’t use the code maybe there will be something you take away from it about PowerShell in general.  Also, since this was posted for CodeShare, please let me know if I could have done something differently or if I completely missed a feature of PowerShell that would made things easier.