PowerShell Remove-Item 不等待

PowerShell Remove-Item not waiting

如果有这段代码

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

有时它可以工作,但有时 New-Item 行会产生 PermissionDenied ItemExistsUnauthorizedAccessError 错误。我认为这意味着之前的 Remove-Item 尚未完全执行并且无法创建文件夹,因为它仍然存在。

如果我在那里插入睡眠

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

然后它总是有效。 如何强制 Remove-Item 以确保文件夹真正被删除? 或者我可能还想念其他东西?

如果您键入 Get-Help Remove-Item -Detailed,您将看到:

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 .

按照规范的建议进行操作:

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

Remove-Item 命令有一个 known issue

试试这个:

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
}

注:

  • Get-ChildItem命令只查找非隐藏的文件和子目录,因此清空目标目录可能不完整;要也包含隐藏项目,请添加 -Force.

  • 类似地,将 -Force 添加到 -RemoveItem 以强制删除设置了 只读 属性的文件。

    • 没有-Force,清空可能再次不完整,但在这种情况下你会得到非终止错误;如果您想将它们视为终止错误,请也添加 -ErrorAction Stop

为了完整起见:您还可以使用安全快速的 .NET 方法:

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

注:

根据您从何处获得 $OUT 的值,您可能希望首先将其转换为完整路径,以确保 .NET 方法删除正确的目录(参见 @mklement0 的评论):

$fullPath = Convert-Path $OUT

更新: 开始于(至少[1])Windows 10版本20H2(不知道WindowsServerversion和build对应的那个;运行winver.exe去查您的版本和构建),DeleteFile Windows API 函数现在表现出 同步 行为,这 隐含地解决了 PowerShell 的问题Remove-Item 和 .NET 的 System.IO.File.Delete / System.IO.Directory.Delete(但是,奇怪的是, 而不是 cmd.exerd /s)。


Remove-Item -Recurse竟然是异步,最终是因为WindowsAPI 文件和目录删除方法本质上是异步的 并且 Remove-Item 没有考虑到这一点。

间歇性地、不可预测地以两种方式之一表现出来:

  • 您的情况:删除后立即重新创建已删除的目录可能会失败,因为在尝试重新创建时删除可能尚未完成。

  • 更典型的是:如果在尝试删除父目录时子目录或文件的删除尚未完成,则删除非空目录本身可能会失败 - 这在ServerFault answer marsze 链接到。

一个潜在的解决方法是通过清空重用一个现有的目录——而不是删除并重新创建它。

但是,清空 Get-ChildItem $OUT -Recurse | Remove-Item -Recurse 的目录容易出现间歇性故障,虽然可能不太常见。

该问题不仅影响 PowerShell 的 Remove-Item,还影响 cmd.exerd /s 以及 .NET 的 [System.IO.Directory]::Delete():

截至 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,Remove-Itemrd /s[System.IO.Directory]::Delete() 都不可靠,因为它们无法解释 [=128] 的异步行为=] API file/directory-removal 函数:

对于提供可靠同步解决方法自定义 PowerShell 函数,请参阅this answer


[1] 我亲自验证了该问题已在版本 20H2 中解决,通过 运行 在 GitHub issue #27958 for hours without failure; suggests that the problem was resolved as early as version 1909, starting with build 18363.657, but Dinh Tran 中的测试发现问题在删除诸如 node_modules 之类的大型目录树时,从构建 18363.1316 开始, 得到解决。我找不到关于这个主题的任何官方信息。