移动项目会导致“文件已存在”错误,尽管文件夹已被删除。

8
我有一些代码,它可以删除一个文件夹,然后将临时目录中的文件复制到该文件夹原本所在的位置。
Remove-Item -Path '.\index.html' -Force
Remove-Item -Path '.\generated' -Force -Recurse  #folder containing generated files

#Start-Sleep -Seconds 10 #uncommenting this line fixes the issue

#$tempDir contains index.html and a sub folder, "generated", which contains additional files.  
#i.e. we're replacing the content we just deleted with new versions.
Get-ChildItem -Path $tempDir | %{
    Move-Item -Path $_.FullName -Destination $RelativePath -Force 
}

我遇到了一个间歇性错误,在generated路径的Move-Item行上出现错误信息:Move-Item : Cannot create a file when that file already exists.

我通过在第二个Remove-Item语句后添加一个hacky的Start-Sleep -Seconds 10来解决这个问题,但这不是一个好的解决方案。

我认为问题在于Remove-Item语句完成/代码继续执行下一行之前,操作系统还没有完成文件删除;但这似乎很奇怪/令人担忧。注意:生成的文件夹中有大约2500个文件(所有文件大小都在1-100 KB之间)。

没有其他进程访问这些文件夹(即使关闭了我的资源管理器窗口并测试了将此目录从我的 AV 中排除),也没有用。

我考虑了其他选项:

  • 使用Copy-Item而不是Move-Item。我不喜欢这个方法,因为它需要在不需要文件时创建新文件(即复制比移动慢)……这比我的当前睡眠hack要快,但仍然不理想。

  • 删除文件而不是文件夹,然后迭代子文件夹并将文件复制到新位置。这个方法可以解决问题,但对于应该简单的事情来说,需要更多的代码;所以我不想选择这个选项。

  • Robocopy可以解决问题;但我更喜欢一个纯PowerShell解决方案。如果没有干净的解决方案,这是我最终会选择的选项。

问题

  • 有人遇到过这种情况吗?
  • 这是一个错误还是我错过了什么?
  • 有人知道修复/好的解决方案吗?

更新

在单独的作业中运行删除操作(即使用下面的代码)无法解决问题。

Start-Job -ScriptBlock {
    Remove-Item -Path '.\index.html' -Force
    Remove-Item -Path '.\generated' -Force -Recurse  #folder containing generated files
} | Wait-Job | Out-Null

#$tempDir contains index.html and a sub folder, "generated", which contains additional files.  
#i.e. we're replacing the content we just deleted with new versions.
Get-ChildItem -Path $tempDir | %{
    Move-Item -Path $_.FullName -Destination $RelativePath -Force 
}

更新 #2

添加这个可以解决问题;即不是等待固定的时间,而是等待路径被删除/每秒检查一次。如果在30秒后路径未被删除,则认为它不会被删除;因此继续进行(这将导致move-item引发一个错误,在其他地方得到处理)。

# ... remove-item code ...
Start-Job -ScriptBlock {
    param($Path)
    while(Test-Path $Path){start-sleep -Seconds 1}
} -ArgumentList '.\generated' | Wait-Job -Timeout 30 | Out-Null
# ... move-item code ...

1
您是否考虑将删除操作作为单独的任务运行?我不知道在自己的内存空间中运行的开销是否会“修复”问题,因为需要花费时间,或者实际上会等到文件被删除。 - Matt
@Matt:只要我能在单独的作业下排队Move-Item(即start-job ... | wait-job | out-null; move-item ...),我就很高兴让它在单独的作业下运行...现在将进行测试以查看是否有所帮助... - JohnLBevan
@Matt:遗憾的是,我尝试了那种方法,但没有成功(请参见我使用的代码的修改后问题)。 - JohnLBevan
@Matt:顺便说一下,我使用了你的建议来调整我的睡眠语句;即利用wait-job上的超时选项等待最大时间,同时每秒检查文件夹并在它消失时继续执行... 还是很hacky,所以我会保留这个问题的开放性;但它确实有效。 - JohnLBevan
1
这可能是一个不错的解决方案。我还会运行 Remove-Job * -force,否则那些挂起的作业就会一直存在。 - Jacob Colvin
1个回答

2
最终我选择了这个解决方案;虽然不完美,但它能够起作用。
Remove-Item -Path '.\index.html' -Force
Remove-Item -Path '.\generated' -Force -Recurse  #folder containing generated files

#wait until the .\generated directory is full removed; or until ~30 seconds has elapsed
1..30 | %{
    if (-not (Test-Path -Path '.\generated' -PathType Container)) {break;}
    Start-Sleep -Seconds 1
}

Get-ChildItem -Path $tempDir | %{
    Move-Item -Path $_.FullName -Destination $RelativePath -Force 
}

这段代码与问题中更新#2中的工作相同; 只是不需要作业的开销; 它只是循环,直到文件被删除。

下面是将上述逻辑封装为可重用cmdlet的代码:

function Wait-Item {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline, HelpMessage = 'The path of the item you wish to wait for')]
        [string]$Path
        ,
        [Parameter(HelpMessage = 'How many seconds to wait for the item before giving up')]
        [ValidateRange(1,[int]::MaxValue)]
        [int]$TimeoutSeconds = 30
        ,
        [Parameter(HelpMessage = 'By default the function waits for an item to appear.  Adding this switch causes us to wait for the item to be removed.')]
        [switch]$Remove
    )
    process {
        [bool]$timedOut = $true
        1..$TimeoutSeconds | %{
            if ((Test-Path -Path $Path) -ne ($Remove.IsPresent)){$timedOut=$false; return;}
            Start-Sleep -Seconds 1
        }
        if($timedOut) {
            Write-Error "Wait-Item timed out after $TimeoutSeconds waiting for item '$Path'"
        }
    }
}

#example usage:
Wait-Item -Path '.\generated' -TimeoutSeconds 30 -Remove

我不确定它是如何工作的。break语句会从循环中跳出,而%Foreach-Object的简写,它不是循环而是命令。因此,break语句将会跳出外部范围中的其他内容,这可能完全出乎意料。 - Andrew Savinykh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接