32

I am trying to delete a directory recursively with rm -Force -Recurse somedirectory, I get several "The directory is not empty" errors. If I retry the same command, it succeeds.

Example:

PS I:\Documents and Settings\m\My Documents\prg\net> rm -Force -Recurse .\FileHelpers
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data\RunTime\_svn: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (_svn:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data\RunTime: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (RunTime:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (Data:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (FileHelpers.Tests:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs\nunit\_svn: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (_svn:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs\nunit: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (nunit:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (Libs:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (I:\Documents an...net\FileHelpers:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
PS I:\Documents and Settings\m\My Documents\prg\net> rm -Force -Recurse .\FileHelpers
PS I:\Documents and Settings\m\My Documents\prg\net>

Of course, this doesn't happen always. Also, it doesn't happen only with _svn directories, and I don't have a TortoiseSVN cache or anything like that so nothing is blocking the directory.

Any ideas?

30

help Remove-Item says:

The Recurse parameter in this cmdlet does not work properly.

and

Because the Recurse parameter in this cmdlet is faulty, the command uses the Get-Childitem cmdlet to get the desire d files, and it uses the pipeline operator to pass them to the Remove-Item cmdlet.

and proposes this alternative as an example:

get-childitem * -include *.csv -recurse | remove-item

So you should pipe get-childitem -recurse into remove-item.

  • Thanks. Just found this thread from 2006: vistax64.com/powershell/… looks like Microsoft is not really interested in fixing this. – Mauricio Scheffer Nov 9 '10 at 20:00
  • @mausch: See this more recent, but still unresolved, reference: Remove-Item -Recurse – Dennis Williamson Nov 9 '10 at 22:21
  • if you do a traversal and delete, you'll have to traverse the child directories first, and their files first. – fschwiet Dec 26 '10 at 22:26
  • 2
    At least the documentation says it doesn't work. – derekerdmann Nov 20 '11 at 4:12
  • 6
    I had to put both -force -recurse flags for Remove-Item, otherwise it kept me prompting "please confirm" Get-ChildItem -Path $Destination -Recurse | Remove-Item -force -recurse – Michael Freidgeim Jan 17 '12 at 6:37
17

@JamesCW: The problem still exists in PowerShell 4.0

I tried another workaround and it worked: use cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
  • 1
    Good old rd /s /q! – JamesCW May 5 '16 at 18:24
  • I've tried every variation of Get-ChildItem; retry loops; calling iisreset before deleting and nothing seems to work reliably. I'm gonna try this one, even though when I first saw it I balked at having DOS inside my Powershell... – Peter McEvoy Aug 23 '16 at 8:04
  • Unfortunately, rd /s fails intermittently too (though seemingly less often than Remove-Item): github.com/Microsoft/console/issues/309 – mklement Nov 27 '18 at 20:45
6

ETA 20181217: PSVersion 4.0 and later will still fail in some circumstances, see alternate answer by Mehrdad Mirreza, and bug report filed by mklement

mklement provides a Proof of Concept solution at this SO answer, as the bug is awaiting an official fix

The new version of PowerShell (PSVersion 4.0) has resolved this issue entirely and Remove-Item "targetdirectory" -Recurse -Force works without any timing problems.

You can check your version by running $PSVersiontable from within the ISE or PowerShell prompt. 4.0 is the version that ships with Windows 8.1 and Server 2012 R2, and it can be installed on previous versions of Windows as well.

  • 4
    Still occurs for me in PowerShell 4.0 – ajbeaven Nov 3 '15 at 19:39
  • 8
    Still occurs in PowerShell v5 !!!!!11!!1!1!!! – Richard Hauer Nov 25 '16 at 8:46
  • @RichardHauer well now I'm just confused – JamesCW Dec 4 '16 at 4:04
  • 2
    @JamesCW I've converted to the rd version. Aside from actually working, it's about 3x faster – Richard Hauer Dec 4 '16 at 4:28
  • The problem has not been fixed as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report. While rd /s may fail less often, it too is broken - see this bug report. – mklement Nov 28 '18 at 14:52
3

The current answer won't actually delete a directory, just its children. Furthermore it will have problems with nested directories as it will again be trying to delete a directory before its contents. I wrote something to delete the files in the correct order, would still have the same problem though sometimes the directory would still be around afterward.

So, now I use something that will catch the exception, wait, and retry (3 times):

For now I'm using this:

function EmptyDirectory($directory = $(throw "Required parameter missing")) {

    if ((test-path $directory) -and -not (gi $directory | ? { $_.PSIsContainer })) {
        throw ("EmptyDirectory called on non-directory.");
    }

    $finished = $false;
    $attemptsLeft = 3;

    do {
        if (test-path $directory) {
            rm $directory -recurse -force
        }

        try {
            $null = mkdir $directory
            $finished = $true
        } 
        catch [System.IO.IOException] {
            Start-Sleep -Milliseconds 500
        }

        $attemptsLeft = $attemptsLeft - 1;
    } 
    while (-not $finished -and $attemptsLeft -gt 0)

    if (-not $finished) {
        throw ("Unable to clean and recreate directory " + $directory)
    }
}
  • 1
    This is good but I still had problems with it. If the mkdir command runs before the system completes the rm command it can throw a System.UnauthorizedAccessException with a FullyQualifiedErrorId of ItemExistsUnauthorizedAccessError. I.e., the directory hasn't been deleted by the OS yet (on my slow HDD). So that error needs to be caught too. And it's a non-terminating error, so the ErrorAction needs to be set to Stop. I also put the rm command in the try block too, just in cases there are transient IO errors when deleting. – Mark Lapierre May 26 '16 at 14:02
  • I can't believe this even has to be done. Damn, Powershell sucks! – jcollum Jul 12 '17 at 16:20
3

To delete the directory and its contents takes two steps. First delete the contents, then the folder itself. Using the workaround for the faulty recursive remove item the solution would look like this:

Get-ChildItem -Path "$folder\\*" -Recurse | Remove-Item -Force -Recurse
Remove-Item $folder

This way you can remove the parent directory as well.

  • 1
    This is exactly what the accepted answer said. Do you have anything to add? – Michael Hampton Dec 14 '13 at 0:11
  • 1
    They are pointing out that the accepted answer does not delete the directory itself, hence it takes two steps. – Paul George May 2 '14 at 15:01
  • 2
    The Remove-Item command your piping into has the same problem that originally stated. It might stumble on a directory item that is non-empty in the same way. – Dejan Feb 26 '16 at 12:16
  • @Dejan This directory could not still be empty if the first line of this code worked, could it? – Ifedi Okonkwo Jun 7 '16 at 14:22
  • While this may decrease the likelihood of failure, it can still fail, given that Remove-Item -Recurse is still involved. The underlying problem still exists as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report. – mklement Nov 28 '18 at 14:55
2

This is what I have working:

$Target = "c:\folder_to_delete"

Get-ChildItem -Path $Target -Recurse -force |
  Where-Object { -not ($_.psiscontainer) } |
   Remove-Item –Force

Remove-Item -Recurse -Force $Target

This first line deletes all files in the tree. The second deletes all the folders including the top.

  • While this may decrease the likelihood of failure, it can still fail, given that Remove-Item -Recurse is still involved. The underlying problem still exists as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report. – mklement Nov 28 '18 at 14:57
2

The existing answers mitigate the problem, so that it occurs less frequently, but they don't address the root cause, which is why failures can still occur.

Remove-Item -Recurse is unexpectedly asynchronous, ultimately because the Windows API methods for file and directory removal are inherently asynchronous and Remove-Item doesn't account for that.

This intermittently, unpredictably manifests in one of two ways:

  • Your case: Removing a nonempty directory itself can fail, if removal of a subdirectory or file in it hasn't completed yet by the time an attempt is made to remove the parent directory.

  • Less commonly: Recreating a removed directory immediately after removal can fail, because the removal may not have completed yet by the time re-creation is attempted.

The problem not only affects PowerShell's Remove-Item, but also cmd.exe's rd /s as well as .NET's [System.IO.Directory]::Delete():

As of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 / cmd.exe 10.0.17134.407 / .NET Framework 4.7.03056, .NET Core 2.1, neither Remove-Item, nor rd /s, nor [System.IO.Directory]::Delete() work reliably, because they fail to account for the asynchronous behavior of the Windows API file/directory-removal functions:

For a custom PowerShell function that provides a reliably synchronous workaround, see this SO answer.

2

Gosh. Lots of answers. I honestly prefer this one over all of them. It's super simple, complete, readable, and works on any Windows machine. It uses .NET's (reliable) recursive delete functionality and if it fails for some reason, it throws a proper exception that can be handled with a try/catch block.

$fullPath = (Resolve-Path "directory\to\remove").ProviderPath
[IO.Directory]::Delete($fullPath, $true)

Note that the Resolve-Path line is important because .NET is not aware of your current directory when resolving relative file paths. That's about the only gotcha I can think of.

0

Take ownership of the files / directories first using Takeown.exe then delete

https://learn-powershell.net/2014/06/24/changing-ownership-of-file-or-folder-using-powershell/

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.