I found this fantastic powershell script
gci -include bin,obj -recurse | remove-item -force -recurse
in a comment by Chris J on this answer
I use this multiple times a day.
How do you prevent this from running through any node_modules folders if they exist or their children?
I've tried various versions of
gci -exclude node_modules -include bin,obj -recurse | remove-item -force -recurse
without success.
UPDATE: After reading @HAL9256's informative answer i realized i failed to include an important requirement. I run this script at the root of my git repo. Therefore, it traverses multiple C# project folders removing the bin and obj folders for all of them. This is only necessary because Visual Studio 2017 & 2019 solution clean doesnt remove everything and as we are in the process of converting from framework to standard/core there are artifacts that get left in the bin and/or obj folder after cleaning that causes things to break when transitioning (probably from core back to framework) a given project via a git branch change.
The root of this issue is the combination of a misuse of recursion combined with filtering. I will go through some examples to illustrate. First we should set up an example directory structure:
Let's try some examples. I will pipe the contents of
Get-ChildItem
into aSelect-Object
to better illustrate what we are retrieving. First, a vanillaGet-ChildItem
:This returns what we expect, equivalent to a
dir
. Now, let's try a-Include
to only "Include" thebin
folder.Hmmm. It returned nothing... We thought that it would take the list of everything, and "only" include the bin folder. What it instead did is interpret the command as,
Get-ChildItem
of, "nothing", (because we didn't specify any files to get), and "Include" of the "nothing", the patternbin
. This makes sense, and is listed in the Docs that "Include needs the Path parameter". Ok. Let's change it to actually get everything*
then do an-Include bin
, then we should get what we want... Right?Hu? I thought with the wildcard, it would include the
bin
folder right? Isn't that how it works?.... well not exactly. Let's change it to-Include
a file instead of a directory:So it looks like the explicit filtering, with a wildcard is for files and not directories. Then how does the original example, where we included directories work?... Well it's because
-Recurse
works differently.-Recurse
changes what we get, it returns everything so theInclude
works:In this example, when we don't have to specify the wildcard
*
filter the-Recurse
implicitly gets everything from the directory. Notice: it included directories that matched the namebin
. Also, notice, it did not include files inside the directories.Why example 1:
Works is because the
Get-ChildItem
returns the directories, and theRemove-Item
with the-Force
and-Recurse
takes care of removing the files.To prove that nothing funky goes on, let's do an
-Exclude node_modules
:Well... that returned more than expected... but not. Notice that it did not return
C:\Temp\node_modules
. This is because-Exclude node_modules
was correct. It excluded the folder specifically named:node_modules
, and included everything else.This is why the second example does not work:
The exclusion filter will exclude the specific folder named
node_modules
, but, due to-Recurse
will-Include
any sub folder that matchesbin
, orobj
. Which of course,node_modules
has a ton of, and hence, doesn't work as we want.So, if
-Include
doesn't work with directories, and-Recurse
goes too deep, what will solve @cResults question? Well, instead of messing around with filters, why don't we just simply ask for the folders that we want like normal?Well this is ok, it returned the contents of the folders, just like the earlier example, the child items and not directories... Well what we really wanted was the folder item so instead of
Get-ChildItem
we useGet-Item
:Ah now, with the switch to
Get-Item
we get the folder, and not the child items inside the folder, which we can now use withRemove-Item
which takes care of the rest: