移动项目原因"文件已经存在"错误,尽管文件夹已被删除

时间:2017-04-21 16:37:15

标签: file powershell directory windows-7-x64 powershell-v5.0

我有一些删除文件夹的代码,然后将文件从临时目录复制到该文件夹​​所在的位置。

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 
}

我在Move-Item : Cannot create a file when that file already exists.路径的Move-Item行上收到间歇性错误generated

我已经能够通过在第二个Start-Sleep -Seconds 10声明后添加hacky Remove-Item来阻止这种情况;虽然这不是一个好的解决方案。

我认为问题是在操作系统赶上实际文件删除之前,Remove-Item语句完成/代码移动到下一行;虽然这看起来很奇怪/令人担忧。注意:生成的文件夹中有大约2,500个文件(全部在1-100 KB之间)。

没有其他进程访问这些文件夹(即我甚至关闭了我的资源管理器窗口,并且已将此目录排除在我的AV之外进行了测试)。

我考虑过其他选择:

  • 使用Copy-Item代替Move-Item。我不喜欢这样,因为它需要在不需要时创建新文件(即副本慢于移动)...它比我目前的睡眠攻击速度快;但仍然不理想。

  • 删除文件&不是文件夹,然后遍历子文件夹&将文件复制到新位置。这可行,但是对于应该简单的事情来说,代码要多得多;所以我不想追求那个选择。

  • 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 个答案:

答案 0 :(得分:0)

最后我决定采用这种解决方案;不完美,但它有效。

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