PowerShell Remove-Item not waiting

我们两清 提交于 2019-12-01 08:49:28
marsze

The Remove-Item command has a known issue.

Try this instead:

if (Test-Path $OUT) 
{ 
    # if exists: empty contents and reuse the directory itself
    Get-ChildItem $OUT -Recurse | Remove-Item -Recurse
}
else
{
    # else: create
    New-Item -ItemType Directory -Path $OUT
}

Note:

  • The Get-ChildItem command only finds non-hidden files and subdirectories, so emptying out the target directory may not be complete; to include hidden items too, add -Force.

  • Similarly, add -Force to -RemoveItem to force removal of files that have the read-only attribute set.

    • Without -Force, emptying may again be incomplete, but you'll get non-terminating errors in this case; if you want to treat them as terminating errors, add -ErrorAction Stop too.

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.

marsze's helpful answer offers a workaround: Instead of recreating the directory, just empty it and reuse it, which bypasses the problem.

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:

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

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

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!