Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell Remove-Item not waiting

If have this piece of code

if(Test-Path -Path $OUT) 
{ 
    Remove-Item $OUT -Recurse 
}
New-Item -ItemType directory -Path $OUT

Sometimes it works, but sometimes the New-Item line produces a PermissionDenied ItemExistsUnauthorizedAccessError error. Which, I assume, means that the previous Remove-Item was not completely performed yet and the folder cannot be created because it still exists.

If I insert a sleep there

if(Test-Path -Path $OUT) 
{ 
    Remove-Item $OUT -Recurse 
    Start-Sleep -s 1
}
New-Item -ItemType directory -Path $OUT

then it works always. How can I force Remove-Item to ensure that the folder is really removed? Or maybe I miss something else?

like image 878
armenm Avatar asked Nov 08 '18 12:11

armenm


People also ask

What is the use of Remove Item in PowerShell?

Remove-Item 1 Description. The Remove-Item cmdlet deletes one or more items. ... 2 Examples. This example deletes all of the files that have names that include a dot (.) from the C:\Test folder. 3 Parameters. Prompts you for confirmation before running the cmdlet. ...

What are the parameters of PowerShell Remove-Item?

Here are the following parameters of PowerShell Remove-Item mention below: Confirm: It is possible that by mistake we can run delete command and we know a delete command can do very blunder, so for such type of situation Confirm will be very useful as it will ask you once before deleting.

How do I delete a file that is read-only in PowerShell?

This command deletes a file that is both hidden and read-only. PowerShell. Remove-Item -Path C:Testhidden-RO-file.txt -Force. It uses the Path parameter to specify the file. It uses the Force parameter to delete it. Without Force, you cannot delete read-only or hidden files.

How to remove items that are not allowed to be removed?

Filter: Here we can specify filter parameters for Remove-item command. Force: Many times when the file is open we could not be able to delete it, so by using the command -Force we can delete it. Here it will give access to remove those items which are not allowed to remove, for example, if there is any file with read-only access ...


3 Answers

Update: Starting with (at least [1]) Windows 10 version 20H2 (I don't know that Windows Server version and build that corresponds to; run winver.exe to check your version and build), the DeleteFile Windows API function now exhibits synchronous behavior, which implicitly solves the problems with PowerShell's Remove-Item and .NET's System.IO.File.Delete / System.IO.Directory.Delete (but, curiously, not with cmd.exe's rd /s).


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: recreating a removed directory immediately after removal can fail, because the removal may not have completed yet by the time re-creation is attempted.

  • More typically: Removing a nonempty directory itself can fail, if removal of a subdirectory or file hasn't completed yet by the time an attempt is made to remove the parent directory - this is demonstrated in the ServerFault answer marsze links to.

A potential workaround is to reuse an existing directory by emptying it - instead of deleting and recreating it.

However, emptying the directory with Get-ChildItem $OUT -Recurse | Remove-Item -Recurse is also susceptible to intermittent failures, though likely less often.

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:

  • PowerShell Core bug report
  • cmd.exe bug report
  • .NET Core bug report

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


[1] I've personally verified that the issue is resolved in version 20H2, by running the tests in GitHub issue #27958 for hours without failure; this answer suggests that the problem was resolved as early as version 1909, starting with build 18363.657, but Dinh Tran finds that the issue is not resolved as of build 18363.1316 when removing large directory trees such as node_modules. I couldn't find any official information on the subject.

like image 184
mklement0 Avatar answered Oct 22 '22 07:10

mklement0


For completeness' sake: You can also use the safe and fast .NET methods:

if ([System.IO.Directory]::Exists($OUT)) {
    [System.IO.Directory]::Delete($OUT, $true)
}
[System.IO.Directory]::CreateDirectory($OUT)

Note:

Depending on where you get the value of $OUT you might want to convert it to a full path first to make sure the .NET methods remove the correct directory (see @mklement0`s comment):

$fullPath = Convert-Path $OUT
like image 24
marsze Avatar answered Oct 22 '22 08:10

marsze


If you type Get-Help Remove-Item -Detailed you'll see:

Example 4: Delete files in subfolders recursively
PS C:\>Get-ChildItem * -Include *.csv -Recurse | Remove-Item

This command deletes all of the CSV files in the current folder and all subfolder recursively.

Because the Recurse parameter in Remove-Item has a known issue, the command in this example uses Get-ChildItem to get the desired files, and then uses the pipeline operator to pass them to Remove-Item .

Do what specification recommends:

if(Test-Path -Path $OUT) 
{ 
    Get-ChildItem $OUT -Recurse | Remove-Item
}
New-Item -ItemType directory -Path $OUT
like image 2
Paweł Dyl Avatar answered Oct 22 '22 06:10

Paweł Dyl