Why Doesn’t Delete Take Wildcards?

Here’s another question from our internal conversion discussion alias that came through last week:

Does the Delete task in MSBuild not use wildcards? The documentation doesn’t say anything about it but that seems to be the behavior I am seeing. I can always create an ItemList then pass that into delete, but I wanted to verify this was “right”.

The person asking the question is correct: the right way to do this is to create an ItemGroup that contains the list of items you want to delete. Generally you’ll do this by specifying each file individually, although you can use a wildcard in the ItemGroup’s Include attribute to pick up files as well. You then pass this ItemGroup into the delete task to perform the delete.

Why do we do it this way? We really wanted to ensure people passed around strongly-typed lists of objects through the build process, so things like metadata can flow through the entire build. If you have an individual task like delete take wildcards, people will tend to use the wildcards in the task directly instead of creating a strongly-typed group first.

Of course, sometimes you can’t know ahead of time what the item group will be, and that’s why we have the CreateItem task (which is, as we’ve found during the conversion of our internal build process, often required and never very pretty…).

[ Author: Neil Enns ]

Comments (18)

  1. Oleg B says:

    Article could be much more useful if it contained example of how to do it properly, because people keep wasting time looking for it.

  2. Alexander M says:

    Here’s a sample I cooked up:


    <TempFiles Include="C:BuildTempDir*" />


    <Target Name="Clean">

    <Delete Files="@(TempFiles)" />


  3. rpreston says:

    This doesn’t seem to be working for me, but I don’t get any errors. Does anyone happen to see why?

    Here’s my target.

    <ItemGroup >

    <FilesToDelete Include="$(ConfigBinDebugDirectory)VCCustomSteps1.*"/>


    <Target Name="CleanVCCustomSteps1" >

    <!– Delete all VCCustomSteps1 output so the build events will be triggered –>


    Condition=" ‘%(ConfigurationToBuild.PlatformToBuild)’==’Win32’ And ‘%(ConfigurationToBuild.FlavorToBuild)’==’Debug’"


    <Output TaskParameter="DeletedFiles" PropertyName="DeletedVCCS1Files" />


    <Message Text="Config bin debug dir = $(ConfigBinDebugDirectory)"/>

    <Message Text="Config bin release dir = $(ConfigBinReleaseDirectory)"/>

    <Message Text="Deleted files: @(DeletedVCCS1Files)"/>


    Here’s the output in the build log:

    Target CleanVCCustomSteps1:

       Config bin debug dir = X:BuildAscentNightlyBuildBuildType..Sources..BinariesWin32Debug

       Config bin release dir = X:BuildAscentNightlyBuildBuildType..Sources..BinariesWin32Release

       Deleted files:

  4. I just use an Exec like this:

    <Exec Command="del wix*.wxs"/>

    Works great and don’t have to put in 5 lines of harder to understand nodes when one clear one will do. I do the same for copy – makes the .proj file a LOT easier to understand and easier to understand means fewer bugs.

  5. Markus Schmidt says:

    I have some experience with NAnt, and because I wanted an easy solution for my co-developers I just wanted to give MSBuild a try for some trivial build task (because it comes with .NET Framework so everybody already has it).

    I stumbled across this issue, and while I could somehow understand why there is no wildcard support for Delete, I think the way ItemGroups were implemented is pretty unusable:

    If I want to copy or delete files that were generated by my build task – which should not only be common but the normal case in a build script! – they won’t get copied or deleted because the wildcard for the ItemGroup’s Includes seems to be resolved at the start of the whole script, when the files are not yet existing, and not at the first or every time the ItemGroup is used.

    At least you comment on CreateTask showed me some kind of workaround, but as you already stated it’s "not very pretty", e. g. because it takes just one Include attribute where I have to put everything in separated by semicolons which of course looks awful and is simply unhandy compared to a list of Include nodes.

    Honestly, I would understand such issues to some degree if you would have been the first to create a build tool like this. But since Ant and NAnt have already been around much longer than MSBuild and seem to do the job much better, I really wonder how it was possible to create such an inefficient solution…

    Too bad, I hoped to see some improvements on NAnt which is far from being perfect as well… but at least behaves consistently.

  6. You can use CreateItem instead of ItemGroups. Here is a reference to Brennan’s blog.

  7. GLM says:

    Hi. I want to accomplish this:

    In an after build target I do copy some ascx files to another folder. In the AfterClean i’d like to delete those too.

    I cant figure out how to tell delete the files in the ItemGroup are on another path.

    CreateItem doesn’t let me modify the name, i tryed concat but it fails.

    Any ideas?

  8. Boomer says:

    Thanks for the tip about using the exec command

    "I just use an Exec like this:

    <Exec Command="del wix*.wxs"/>"

    However, I get a strange behavior in VS 2008.

    The following works fine

    <Exec Command="del $(ReleaseDirectory)*.txt" />

    But it won’t take a double wildcard

    <Exec Command="del $(ReleaseDirectory)*.*" />

    or a single wildcard like this

    <Exec Command="del $(ReleaseDirectory)*" />


    The command "del ProjectRelease*.*" exited with code 1.

    I don’t know if I am doing something wrong, but I just thought I would mention this in case anyone else sees this behavior.

  9. kruss1971 says:


    to delete directory/* or directory/*.*, you are basically deleting the directory

    <RemoveDir Directories="$(OutputPath)dir" />

    value can be semi-colon delimited string of directories

    if you need to add it back in, just use

    <MakeDir Directories="$(OutputPath)dir" />

    now you’ve only used two lines, got around the exec bug, and kept yourself from using all of the steps needed for ItemGroup

  10. Mike says:

    This post in incorrect, if you use wildcards in the include attribute of a tag in an itemgroup, the delete task will not work either!

  11. Jeff Yates says:

    @Mike: Working fine for me. Just used Alexander M’s example.

  12. n/a says:

    It looks like

    <Exec Command="del $(ReleaseDirectory)*.txt" />

    doesn’t work because the $(…) evaluates to an empty string so the result becomes "del *.txt"

    I tried "del @(ReleaseDirectory)*.txt" but it doesn’t work because @(…) evaluates to everything that matches so the result becomes something like

    "del readme.txt;otherfile.txt;echelon.txt"

    which doesn’t work.

    I still havn’t found out how delete with wildcards in msbuild.

  13. Michael Bunney says:

    Your wildcard examples are failing because by default the DEL command prompts for confirmation.  If you add the /Q option it will succeed.  You can also add the /S option to make it recursive and /F to force it to delete read-only files:

    <Exec Command="del /Q /F /S $(ReleaseDirectory)*.*" />

    This won’t delete subdirectories, though.

  14. Franz Wong says:

    I have done with the following, but it seems nothing is deleted.


     <BinDirectories Include="$(dist)samplesrc**bin"/>

     <ObjDirectories Include="$(dist)samplesrc**obj"/>


    <RemoveDir Directories="@(BinDirectories)"/>

    <RemoveDir Directories="@(ObjDirectories)"/>

  15. Eric Swanson says:

    Wow! Did any of you test what you were suggesting? 🙂  The "Delete" task works just like "<Exec Command="del …" /> and this will only delete files, leaving the directory and any sub-directories. The "RemoveDir" task would be valid, but will error if the directory is not empty. To fix all of the suggestions for someone who is attempting to wipe-out an entire directory, use the following:

    <Exec Command="RMDIR /S /Q {PATH}" />  … where {PATH} is your path.

    For example, I need to delete some content files (MVC Views) that get dumped in the /bin directory from a badly coded "Preview" assembly from Microsoft :P. So, I add the task to an "AfterBuild" target… <Exec Command="RMDIR /S /Q $(OutputPath)Views" /> to delete the "Views" directory and anything inside it that is copied by the build into the "bin" directory.

  16. Kelvin. says:


    <TempFiles Include="C:BuildTempDir*" />


    <Target Name="Clean">

    <Delete Files="@(TempFiles)" />


    This works for me.