2017-04-21 152 views
2

我有一些代碼刪除一個文件夾,然後將文件從臨時目錄複製到該文件夾​​的位置。Move-Item導致「文件已存在」錯誤,儘管文件夾已被刪除

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.上爲generated路徑上移,項目線。

我已經能夠通過在第二個Remove-Item聲明後添加hacky Start-Sleep -Seconds 10來阻止此問題;儘管這不是一個好的解決方案。

我認爲問題在於操作系統已經趕上實際的文件刪除之前,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

你會考慮運行去除作爲單獨的作業?我不知道在自己的內存空間中運行的開銷是否會「解決」問題,因爲需要花費時間,或者是否真的要等到文件被刪除。 – Matt

+0

@Matt:只要我可以將Move-Item排列在該作業完成後面(即'start-job ... | wait-job | out-null; move -item ...')...現在測試看看是否有幫助... – JohnLBevan

+0

@Matt:可悲的是,沒有喜歡這種方法(請參閱我使用的代碼的修正問題)。 – JohnLBevan

回答

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中的工作相同;只是不需要工作的開銷;只是循環,直到文件被刪除。

下面是上述邏輯包裝成reuable的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 
+0

我不確定它是如何工作的。 'break'從循環中斷開''''是'Foreach-Object'的縮寫,它不是一個循環,而是一個cmdlet。因此,'break'語句會在外部範圍內爆發*其他*,這可能是完全意想不到的。 –

+0

好點;修改'break'爲'return'。 – JohnLBevan

相關問題