我有一些删除文件夹的代码,然后将文件从临时目录复制到该文件夹所在的位置。
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 ...
答案 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