บังคับให้ลบไฟล์และไดเรกทอรีใน PowerShell บางครั้งล้มเหลว แต่ไม่เสมอไป


33

ฉันพยายามลบไดเรกทอรีซ้ำด้วย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>

แน่นอนว่ามันไม่ได้เกิดขึ้นเสมอไป นอกจากนี้มันไม่ได้เกิดขึ้นเฉพาะกับ_svnไดเรกทอรีและฉันไม่มีTortoiseSVNแคชหรืออะไรทำนองนั้นดังนั้นไม่มีอะไรขวางกั้นไดเรกทอรี

ความคิดใด ๆ

คำตอบ:


31

help Remove-Item พูดว่า:

พารามิเตอร์ Recurse ใน cmdlet นี้ทำงานไม่ถูกต้อง

และ

เนื่องจากพารามิเตอร์ Recurse ใน cmdlet นี้มีข้อบกพร่องคำสั่งใช้ Get-Childitem cmdlet เพื่อรับไฟล์ปรารถนา d และใช้ผู้ดำเนินการไปป์ไลน์เพื่อส่งไปยัง cmdlet ลบรายการ

และเสนอทางเลือกนี้เป็นตัวอย่าง:

get-childitem * -include *.csv -recurse | remove-item

ดังนั้นคุณควรท่อเข้าไปในget-childitem -recurseremove-item


ขอบคุณ เพิ่งพบกระทู้นี้จากปี 2006: vistax64.com/powershell/ …ดูเหมือนว่า Microsoft ไม่สนใจที่จะแก้ไขปัญหานี้
Mauricio Scheffer

@mausch: ดูข้อมูลนี้ล่าสุด แต่ยังไม่ได้รับการแก้ไขอ้างอิง: ลบรายการ
หยุดชั่วคราวจนกว่าจะมีประกาศเพิ่มเติม

หากคุณทำการสำรวจเส้นทางและลบคุณจะต้องสำรวจไดเรกทอรีลูกก่อนและไฟล์ก่อน
fschwiet

2
อย่างน้อยเอกสารบอกว่ามันไม่ทำงาน
derekerdmann

6
ฉันต้องใส่ทั้ง -force -recurse flag สำหรับ Remove-Item มิฉะนั้นจะทำให้ฉันแจ้งเตือน "โปรดยืนยัน" Get-ChildItem -Path $ Destination -Recurse ลบรายการ - บังคับ
ชดเชย

17

@JamesCW: ปัญหายังคงมีอยู่ใน PowerShell 4.0

ฉันลองวิธีแก้ปัญหาอื่นและใช้งานได้: ใช้ cmd.exe:

&cmd.exe /c rd /s /q $somedirectory

1
ดี rd / s / q!
JamesCW

ฉันได้ลองใช้ชุดรูปแบบของ Get-ChildItem ทุกครั้ง ลองซ้ำลูป โทรiisresetก่อนที่จะลบและไม่มีอะไรดูเหมือนว่าจะทำงานได้อย่างน่าเชื่อถือ ฉันจะลองอันนี้แม้ว่าฉันจะได้เห็นมันครั้งแรกฉันก็รู้สึกอึดอัดใจที่มีดอสอยู่ภายใน Powershell ของฉัน ...
Peter McEvoy

น่าเสียดายที่rd /sล้มเหลวเป็นระยะเช่นกัน (แต่ดูเหมือนว่าจะน้อยกว่าRemove-Item): github.com/Microsoft/console/issues/309
mklement

ไม่ชอบเครื่องหมายทับด้วยคสำหรับฉัน คุณต้องนำหน้ามันโดย powershell - คำสั่งและอ้างส่วน cmd.exe? ฉันได้รับ "คุณต้องระบุค่านิพจน์หลังจากตัวดำเนินการ '/' "โทเค็นที่ไม่คาดคิด 'c' ในการแสดงออกหรือคำสั่งมันเหมือนกันกับ powershell - คำสั่งที่อยู่ด้านหน้ามัน / จำเป็นต้องหลบหนีหรือไม่
มิเชล

7

ETA 20181217: PSVersion 4.0 และใหม่กว่าจะยังคงล้มเหลวในบางสถานการณ์ดูคำตอบอื่นโดยMehrdad Mirrezaและรายงานข้อผิดพลาดที่ยื่นโดยmklement

mklementมอบโซลูชัน Proof of Concept ที่คำตอบ SOนี้เนื่องจากข้อบกพร่องกำลังรอการแก้ไขอย่างเป็นทางการ

เวอร์ชันใหม่ของPowerShell( PSVersion 4.0) ได้แก้ไขปัญหานี้แล้วและRemove-Item "targetdirectory" -Recurse -Forceทำงานได้โดยไม่มีปัญหาเรื่องเวลา

คุณสามารถตรวจสอบเวอร์ชั่นของคุณโดยเรียกใช้$PSVersiontableจากภายใน ISE หรือPowerShellพร้อมท์ 4.0 เป็นรุ่นที่มาพร้อมกับWindows 8.1และServer 2012 R2สามารถติดตั้งบน Windows รุ่นก่อนหน้าได้เช่นกัน


5
ยังคงเกิดขึ้นกับฉันใน PowerShell 4.0
ajbeaven

10
ยังคงเกิดขึ้นใน PowerShell v5 !!!!! 11 !! 1! 1 !!!
Richard Hauer

@ RichardHauer ดีตอนนี้ฉันแค่สับสน
JamesCW

2
@JamesCW ฉันได้แปลงเป็นrdเวอร์ชั่นแล้ว นอกเหนือจากการทำงานจริงแล้วมันเร็วกว่าประมาณ 3 เท่า
Richard Hauer

ปัญหายังไม่ได้รับการแก้ไขตั้งแต่ Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - ดูรายงานข้อผิดพลาดนี้ ในขณะที่rd /sอาจล้มเหลวน้อยลงมันก็พัง - ดูรายงานข้อผิดพลาดนี้
mklement

4

ปรับปรุง : ดูเหมือนว่ามีแผนการที่จะทำให้การกำจัดของ Windows APIs ระบบแฟ้มรายการซิงโคร แต่พวกเขาไม่ได้ซิงโครยังเป็นของวินโดวส์ 10 รุ่น 1903 - ดูความคิดเห็นนี้บน GitHub


คำตอบที่มีอยู่ช่วยลดปัญหาเพื่อให้เกิดขึ้นน้อยลง แต่พวกเขาไม่ได้ระบุสาเหตุที่แท้จริงซึ่งเป็นสาเหตุที่ความล้มเหลวยังคงสามารถเกิดขึ้นได้

Remove-Item -Recurseไม่ตรงกันโดยไม่คาดคิดท้ายที่สุดเนื่องจากวิธีการWindows API สำหรับการลบไฟล์และไดเรกทอรีนั้นไม่ตรงกันโดยเนื้อแท้และRemove-Itemไม่ได้คำนึงถึงสิ่งนั้น

สิ่งนี้เกิดขึ้นเป็นระยะ ๆ อย่างไม่อาจคาดการณ์ได้ในหนึ่งในสองวิธี:

  • กรณีของคุณ: การลบไดเรกทอรีที่ไม่ได้รับอนุญาตนั้นอาจล้มเหลวได้หากการลบไดเรกทอรีย่อยหรือไฟล์ในนั้นยังไม่เสร็จสิ้นเมื่อถึงเวลาที่มีความพยายามที่จะลบไดเรกทอรีหลัก

  • น้อยกว่าปกติ: การสร้างไดเรกทอรีที่ถูกลบใหม่ในทันทีหลังจากการลบอาจล้มเหลวได้เนื่องจากการลบอาจยังไม่เสร็จตามเวลาที่พยายามสร้างใหม่

ปัญหาไม่เพียง แต่ส่งผลกระทบต่อ PowerShell ของRemove-Itemแต่ยังcmd.exeเป็นrd /sเช่นเดียวกับ .NET ของ[System.IO.Directory]::Delete() :

ในฐานะของ Windows PowerShell v5.1 / PowerShell หลัก 6.2.0-preview.1 / cmd.exe10.0.17134.407 / .NET Framework 4.7.03056, .NET หลัก 2.1 ค่าRemove-Itemหรือrd /sไม่[System.IO.Directory]::Delete()ทำงานได้อย่างน่าเชื่อถือเพราะพวกเขาล้มเหลวในการบัญชีสำหรับตรงกัน พฤติกรรมของฟังก์ชั่นการลบไฟล์ / ไดเรกทอรี Windows API :

สำหรับฟังก์ชั่น PowerShell ที่กำหนดเองที่ให้วิธีแก้ปัญหาแบบซิงโครนัสที่เชื่อถือได้ดูคำตอบ SOนี้


เมื่อจัดการกับไฟล์ที่ลบคือบาง:while($true) { if ( (Remove-Item [...] *>&1) -ne $null) { Start-Sleep 0.5 } else { break } }
Farway

3

คำตอบปัจจุบันจะไม่ลบไดเรกทอรีเพียงลูก ๆ ของมัน นอกจากนี้จะมีปัญหากับไดเรกทอรีที่ซ้อนกันเนื่องจากจะพยายามลบไดเรกทอรีก่อนเนื้อหา ฉันเขียนบางอย่างเพื่อลบไฟล์ตามลำดับที่ถูกต้องจะยังคงมีปัญหาเดียวกันแม้ว่าบางครั้งไดเรกทอรีจะยังคงอยู่ในภายหลัง

ดังนั้นตอนนี้ฉันใช้บางสิ่งบางอย่างที่จะจับข้อยกเว้นรอและลองอีกครั้ง (3 ครั้ง):

ตอนนี้ฉันใช้สิ่งนี้:

function EmptyDirectory($directory = $(throw "Required parameter missing")) {

    if ((test-path $directory) -and -not (gi $directory | ? { $_.PSIsContainer })) {
        throw ("EmptyDirectory called on non-directory.");
    }

    $finished = $false;
    $attemptsLeft = 3;

    do {
        if (test-path $directory) {
            rm $directory -recurse -force
        }

        try {
            $null = mkdir $directory
            $finished = $true
        } 
        catch [System.IO.IOException] {
            Start-Sleep -Milliseconds 500
        }

        $attemptsLeft = $attemptsLeft - 1;
    } 
    while (-not $finished -and $attemptsLeft -gt 0)

    if (-not $finished) {
        throw ("Unable to clean and recreate directory " + $directory)
    }
}

1
นี่เป็นสิ่งที่ดี แต่ฉันยังคงมีปัญหากับมัน หากคำสั่ง mkdir รันก่อนที่ระบบจะเสร็จสิ้นคำสั่ง rm สามารถโยน System.UnauthorizedAccessException ด้วย FullyQualifiedErrorId ของ ItemExistsUnauthorizedAccessError นั่นคือระบบปฏิบัติการยังไม่ได้ลบไดเรกทอรี (ใน HDD ที่ช้าของฉัน) ดังนั้นข้อผิดพลาดนั้นจะต้องถูกจับเช่นกัน และเป็นข้อผิดพลาดที่ไม่สิ้นสุดดังนั้น ErrorAction จะต้องตั้งค่าเป็น Stop ฉันยังใส่คำสั่ง rm ในบล็อกลองด้วยเช่นกันในกรณีที่มีข้อผิดพลาด IO ชั่วคราวเมื่อลบ
Mark Lapierre

ฉันไม่อยากจะเชื่อเลยว่าสิ่งนี้จะต้องทำ ประณาม Powershell ครับ!
jcollum

3

ในการลบไดเรกทอรีและเนื้อหาในสองขั้นตอน ก่อนอื่นให้ลบเนื้อหาจากนั้นจึงทำการสร้างโฟลเดอร์เอง การใช้วิธีแก้ปัญหาสำหรับรายการลบแบบเรียกซ้ำที่ผิดพลาดโซลูชันจะมีลักษณะดังนี้

Get-ChildItem -Path "$folder\\*" -Recurse | Remove-Item -Force -Recurse
Remove-Item $folder

วิธีนี้คุณสามารถลบไดเรคทอรีหลักได้เช่นกัน


1
นี่คือสิ่งที่คำตอบที่ได้รับการยอมรับกล่าวว่า คุณมีอะไรเพิ่มหรือไม่
Michael Hampton

1
พวกเขากำลังชี้ให้เห็นว่าคำตอบที่ยอมรับไม่ได้ลบไดเรกทอรีตัวเองจึงใช้เวลาสองขั้นตอน
Paul George

2
Remove-Itemคำสั่งของคุณลงในท่อมีปัญหาเดียวกันกับที่ระบุไว้ มันอาจสะดุดกับรายการไดเรกทอรีที่ไม่ว่างเปล่าในทางเดียวกัน
Dejan

@Dejan ไดเรกทอรีนี้ยังไม่ว่างถ้าบรรทัดแรกของรหัสนี้ใช้งานได้ใช่ไหม
Ifedi Okonkwo

1
แม้ว่าสิ่งนี้อาจลดโอกาสเกิดความล้มเหลว แต่ก็ยังสามารถล้มเหลวได้เนื่องจากRemove-Item -Recurseยังมีส่วนเกี่ยวข้องอยู่ ปัญหาพื้นฐานยังคงมีอยู่ใน Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - ดูรายงานข้อผิดพลาดนี้
mklement

3

เอ้ย คำตอบมากมาย ฉันชอบอันนี้มากกว่าทุกคน มันง่ายมากสมบูรณ์อ่านได้และทำงานบนเครื่อง Windows ทุกเครื่อง มันใช้ฟังก์ชั่นการลบซ้ำของ. NET (ที่เชื่อถือได้) และหากล้มเหลวด้วยเหตุผลบางอย่างมันจะส่งข้อยกเว้นที่เหมาะสมที่สามารถจัดการได้ด้วยบล็อกลอง / catch

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

โปรดสังเกตว่าResolve-Pathบรรทัดนั้นมีความสำคัญเนื่องจาก. NET ไม่ทราบถึงไดเรกทอรีปัจจุบันของคุณเมื่อแก้ไขพา ธ ของไฟล์ที่เกี่ยวข้อง นั่นเป็นเพียง gotcha เดียวที่ฉันนึกได้


2

นี่คือสิ่งที่ฉันทำงาน:

$Target = "c:\folder_to_delete"

Get-ChildItem -Path $Target -Recurse -force |
  Where-Object { -not ($_.psiscontainer) } |
   Remove-Item Force

Remove-Item -Recurse -Force $Target

บรรทัดแรกนี้จะลบไฟล์ทั้งหมดในทรี ครั้งที่สองจะลบโฟลเดอร์ทั้งหมดรวมถึงด้านบน


แม้ว่าสิ่งนี้อาจลดโอกาสเกิดความล้มเหลว แต่ก็ยังสามารถล้มเหลวได้เนื่องจากRemove-Item -Recurseยังมีส่วนเกี่ยวข้องอยู่ ปัญหาพื้นฐานยังคงมีอยู่ใน Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - ดูรายงานข้อผิดพลาดนี้
mklement


0

ฉันมีปัญหานี้กับไดเรกทอรีที่จะไม่ลบ ฉันพบว่าหนึ่งในโฟลเดอร์ย่อยเสียหายและเมื่อฉันพยายามย้ายหรือเปลี่ยนชื่อเด็กคนนั้นฉันได้รับข้อความแสดงข้อผิดพลาดว่ามีบางอย่างหายไป ฉันพยายามใช้ rm -Force และได้รับข้อผิดพลาดเดียวกับที่คุณทำ

สิ่งที่ใช้ได้ผลสำหรับฉันคือการบีบอัด dir พาเรนต์โดยใช้ 7-zip พร้อมตัวเลือก "ลบไฟล์หลังจากการบีบอัด" เมื่อมันถูกบีบอัดฉันสามารถลบไฟล์ zip ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.