在 PowerShell 中强制删除文件和目录有时会失败,但并非总是如此

Mau*_*fer 36 powershell

我正在尝试使用 递归删除一个目录rm -Force -Recurse somedirectory,我收到了几个“目录不是空的”错误。如果我重试相同的命令,它会成功。

例子:

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>
Run Code Online (Sandbox Code Playgroud)

当然,这并不总是发生。此外,它不仅仅发生在_svn目录中,而且我没有TortoiseSVN缓存或类似的东西,所以没有任何东西阻止目录。

有任何想法吗?

Den*_*son 35

help Remove-Item 说:

此 cmdlet 中的 Recurse 参数无法正常工作。

由于此 cmdlet 中的 Recurse 参数有问题,因此该命令使用 Get-Childitem cmdlet 获取所需的 d 文件,并使用管道运算符将它们传递给 Remove-Item cmdlet。

并以这个替代方案为例:

get-childitem * -include *.csv -recurse | remove-item
Run Code Online (Sandbox Code Playgroud)

所以你应该管道get-childitem -recurse进入remove-item.

  • 我必须为 Remove-Item 设置两个 -force -recurse 标志,否则它让我提示“请确认” Get-ChildItem -Path $Destination -Recurse | 删除项目 -force -recurse (8认同)
  • 至少文档说它不起作用。 (2认同)
  • 对我不起作用,我仍然收到错误 (2认同)

Meh*_*eza 21

@JamesCW:问题在 PowerShell 4.0 中仍然存在

我尝试了另一种解决方法,它奏效了:使用 cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,`rd /s` 也会间歇性地失败(虽然看起来比 `Remove-Item` 少):https://github.com/Microsoft/console/issues/309 (2认同)

mkl*_*ent 9

更新从 Windows 10 版本开始1909,(至少)构建18363.657(我不知道 对应的Windows Server版本和构建;运行winver.exe以检查您的版本和构建),DeleteFileWindows API 函数现在表现出同步行为,这隐式解决了问题使用PowerShell的Remove-Item和.NET的System.IO.File.Delete/ System.IO.Directory.Delete(但是,奇怪的是,不能cmd.exerd /s)。


现有的答案缓解了问题,使其发生的频率降低,但它们没有解决根本原因,这就是故障仍然可能发生的原因。

Remove-Item -Recurse出乎意料地是异步的,最终是因为用于文件和目录删除Windows API 方法本质上是异步的Remove-Item并且没有考虑到这一点。

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

  • 您的情况:如果在尝试删除父目录时尚未完成删除其中的子目录或文件,则删除非空目录本身可能会失败。

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

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

从 Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 / cmd.exe10.0.17134.407 / .NET Framework 4.7.03056 开始,.NET Core 2.1既不是Remove-Item,也不是,也rd /s不能[System.IO.Directory]::Delete()可靠地工作,因为它们没有考虑异步Windows API 文件/目录删除函数的行为

有关提供可靠同步解决方法自定义 PowerShell 函数,请参阅此 SO 答案


Jam*_*sCW 8

ETA 20181217:PSVersion 4.0 及更高版本在某些情况下仍会失败,请参阅Mehrdad Mirreza 的替代答案和mklement提交的错误报告

mklement在此SO 答案中提供了概念证明解决方案,因为该错误正在等待官方修复

PowerShell( PSVersion 4.0)的新版本完全解决了这个问题,并且Remove-Item "targetdirectory" -Recurse -Force没有任何计时问题。

您可以通过$PSVersiontable从 ISE 或PowerShell提示中运行来检查您的版本。4.0 是附带Windows 8.1和的版本,Server 2012 R2它也可以安装在以前版本的 Windows 上。

  • 在 PowerShell v5 中仍然出现!!!!!!11!!1!1!!! (11认同)
  • 在 PowerShell 4.0 中对我来说仍然发生 (5认同)
  • @JamesCW 我已转换为 `rd` 版本。除了实际工作之外,它的速度大约快了 3 倍 (2认同)
  • 自 Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 起,该问题_未_得到修复 - 请参阅 [此错误报告](https://github.com/PowerShell/PowerShell/issues/8211)。虽然 `rd /s` 可能失败的次数较少,但它也已损坏 - 请参阅 [此错误报告](https://github.com/Microsoft/console/issues/309)。 (2认同)

Phi*_*hil 5

天哪。很多答案。老实说,我比所有这些都更喜欢这个。它超级简单、完整、可读,并且可以在任何 Windows 机器上运行。它使用 .NET 的(可靠)递归删除功能,如果由于某种原因失败,它会抛出一个可以使用 try/catch 块处理的适当异常。

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

请注意,该Resolve-Path行很重要,因为 .NET 在解析相对文件路径时不知道您的当前目录。这是我能想到的唯一问题。