Wymuszone usuwanie plików i katalogów w PowerShell czasami zawiedzie, ale nie zawsze

33

Próbuję usunąć katalog rekurencyjnie rm -Force -Recurse somedirectory, otrzymuję kilka błędów „Katalog nie jest pusty”. Jeśli powtórzę to samo polecenie , to się powiedzie.

Przykład:

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>

Oczywiście nie zawsze tak się dzieje . Ponadto nie dzieje się tak tylko w przypadku _svnkatalogów i nie mam pamięci podręcznej TortoiseSVN ani niczego podobnego, więc nic nie blokuje katalogu.

Jakieś pomysły?

Mauricio Scheffer
źródło

Odpowiedzi:

31

help Remove-Item mówi:

Parametr Recurse w tym poleceniu cmdlet nie działa poprawnie.

i

Ponieważ parametr Recurse w tym poleceniu cmdlet jest wadliwy, polecenie używa polecenia cmdlet Get-Childitem, aby uzyskać pożądane pliki, i używa operatora potoku do przekazania ich do polecenia cmdlet Remove-Item.

i proponuje tę alternatywę jako przykład:

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

Więc powinien rurę get-childitem -recursedo remove-item.

Wstrzymano do odwołania.
źródło
Dzięki. Właśnie znalazłem ten wątek z 2006 roku: vistax64.com/powershell/… wygląda na to, że Microsoft nie jest tak naprawdę zainteresowany naprawieniem tego.
Mauricio Scheffer,
@mausch: Zobacz to nowsze, ale wciąż nierozwiązane, odniesienie: Remove-Item -Recurse
Wstrzymane do odwołania.
jeśli przejdziesz i skasujesz, najpierw będziesz musiał przejrzeć katalogi podrzędne, a najpierw ich pliki.
fschwiet
2
Przynajmniej dokumentacja mówi, że to nie działa.
derekerdmann
6
Musiałem umieścić obie flagi -force -recurse dla elementu Remove-Item, w przeciwnym razie pojawiał się monit „proszę potwierdzić” Get-ChildItem -Path $ Destination -Recurse | Remove-Item -force -recurse
MiFreidgeim SO-przestań być zły
17

@JamesCW: Problem nadal występuje w PowerShell 4.0

Próbowałem innego obejścia i zadziałało: użyj cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
Mehrdad Mirreza
źródło
1
Dobry stary rd / s / q!
JamesCW,
Wypróbowałem każdą odmianę Get-ChildItem; spróbuj ponownie; dzwonienie iisresetprzed usunięciem i nic nie działa niezawodnie . Spróbuję tego, chociaż kiedy go zobaczyłem, nie chciałem mieć DOS-a wewnątrz mojej PowerShell ...
Peter McEvoy
Niestety, rd /srównież zawiesza się sporadycznie (choć pozornie rzadziej niż Remove-Item): github.com/Microsoft/console/issues/309
2018
Dla mnie to nie lubi cięcia przez c. Czy musisz poprzedzić to komendą PowerShell-a i zacytować część cmd.exe? Otrzymuję komunikat „Musisz podać wyrażenie wartości po operatorze„ / ”.” „Nieoczekiwany token„ c ”w wyrażeniu lub stwierdzeniu. To samo z komendą PowerShell przed nim. Czy / musi uciekać?
Michele
7

ETA 20181217: W niektórych okolicznościach PSVersion 4.0 i nowsze nadal będą zawierane, zobacz alternatywną odpowiedź Mehrdada Mirrezy i raport o błędach złożony przez mklement

mklement zapewnia rozwiązanie Proof of Concept przy tej odpowiedzi SO , ponieważ błąd oczekuje na oficjalną poprawkę

Nowa wersja PowerShell( PSVersion 4.0) całkowicie rozwiązała ten problem i Remove-Item "targetdirectory" -Recurse -Forcedziała bez problemów z synchronizacją.

Możesz sprawdzić swoją wersję, uruchamiając $PSVersiontablez poziomu ISE lub PowerShellmonitu. 4.0 to wersja dostarczana z Windows 8.1i Server 2012 R2, i można ją również zainstalować w poprzednich wersjach systemu Windows.

JamesCW
źródło
5
Nadal występuje dla mnie w PowerShell 4.0
ajbeaven
10
Nadal występuje w programie PowerShell v5 !!!!! 11 !! 1! 1 !!!
Richard Hauer,
@RichardHauer dobrze, teraz jestem tylko zdezorientowany
JamesCW
2
@JamesCW Konwertowałem do rdwersji. Poza faktycznym działaniem jest około 3 razy szybszy
Richard Hauer,
Problem nie został rozwiązany od wersji Windows PowerShell 5.1 / PowerShell Core 6.2.0-Preview.1 - zobacz ten raport o błędzie . Chociaż rd /srzadziej może zawieść, to też jest zepsuty - zobacz ten raport o błędzie .
mklement
4

Aktualizacja : pozornie istnieją plany synchronizacji interfejsów API usuwania systemu plików Windows, ale nie są one jeszcze synchroniczne od wersji Windows 10 1903 - zobacz ten komentarz na GitHub .


Istniejące odpowiedzi łagodzą problem, dzięki czemu występuje rzadziej, ale nie dotyczą pierwotnej przyczyny , dlatego nadal mogą występować awarie.

Remove-Item -Recursejest nieoczekiwanie asynchroniczny , ponieważ metody Windows API do usuwania plików i katalogów są z natury asynchroniczne i Remove-Itemnie uwzględniają tego.

Ten sposób przerywany, nieprzewidywalnie przejawiająca się w jednej z dwóch sposobów:

  • Twój przypadek: Samo usunięcie niepustego katalogu może się nie powieść, jeśli usunięcie podkatalogu lub pliku w nim jeszcze się nie zakończyło do czasu próby usunięcia katalogu nadrzędnego.

  • Rzadziej: odtwarzanie usuniętego katalogu natychmiast po usunięciu może się nie powieść, ponieważ usunięcie mogło nie zostać jeszcze ukończone do czasu próby odtworzenia.

Problem nie dotyczy tylko PowerShell użytkownika Remove-Item, ale również cmd.exe„s rd /sjak .NET na[System.IO.Directory]::Delete() :

Począwszy od Windows PowerShell v5.1 / PowerShell Core 6.2.0-Preview.1 / cmd.exe10.0.17134.407 / .NET Framework 4.7.03056, .NET Core 2.1, ani nie Remove-Item, ani rd /snie [System.IO.Directory]::Delete()działają niezawodnie , ponieważ nie uwzględniają asynchronicznego zachowanie funkcji usuwania plików / katalogów Windows API :

Aby uzyskać niestandardową funkcję programu PowerShell, która zapewnia niezawodne synchroniczne obejście , zobacz tę odpowiedź SO .

mklement
źródło
Podczas obsługi plików, których usunięcie jest pewne:while($true) { if ( (Remove-Item [...] *>&1) -ne $null) { Start-Sleep 0.5 } else { break } }
Farway
3

Obecna odpowiedź nie spowoduje usunięcia katalogu, tylko jego dzieci. Ponadto będzie miał problemy z zagnieżdżonymi katalogami, ponieważ ponownie będzie próbował usunąć katalog przed jego zawartością. Napisałem coś, aby usunąć pliki we właściwej kolejności, nadal miałbym ten sam problem, chociaż czasami katalog nadal byłby w pobliżu.

Więc teraz używam czegoś, co złapie wyjątek, poczekaj i ponów próbę (3 razy):

Na razie używam tego:

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)
    }
}
fschwiet
źródło
1
To dobrze, ale nadal miałem z tym problemy. Jeśli komenda mkdir zostanie uruchomiona przed wykonaniem przez system komendy rm, może zgłosić wyjątek System.UnauthorizedAccessException z wartością FullyQualifiedErrorId z ItemExistsUnauthorizedAccessError. Tj. Katalog nie został jeszcze usunięty przez system operacyjny (na moim wolnym dysku twardym). Więc ten błąd również musi zostać wykryty. Jest to błąd nie kończący się, dlatego ErrorAction należy ustawić na Stop. Umieszczam również komendę rm w bloku try, na wypadek, gdyby podczas usuwania wystąpiły przejściowe błędy We / Wy.
Mark Lapierre
Nie mogę uwierzyć, że trzeba to zrobić. Cholera, Powershell jest do bani!
jcollum
3

Aby usunąć katalog i jego zawartość, należy wykonać dwa kroki. Najpierw usuń zawartość, a następnie sam folder. Korzystając z obejścia wadliwego rekurencyjnego elementu usuwającego, rozwiązanie wyglądałoby następująco:

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

W ten sposób możesz również usunąć katalog nadrzędny.

Carl Baker
źródło
1
Tak właśnie powiedziała zaakceptowana odpowiedź. Czy masz coś do dodania?
Michael Hampton
1
Wskazują, że zaakceptowana odpowiedź nie usuwa samego katalogu, dlatego zajmuje dwa kroki.
Paul George
2
Remove-ItemPolecenia swoje rurociągi do ma ten sam problem, który pierwotnie podane. Może się potknąć o element katalogu, który nie jest pusty w ten sam sposób.
Dejan
@Dejan Ten katalog nie mógłby być pusty, jeśli działała pierwsza linia tego kodu, prawda?
Ifedi Okonkwo
1
Chociaż może to zmniejszyć prawdopodobieństwo niepowodzenia, może ono nadal zawieść, biorąc pod uwagę, że Remove-Item -Recursenadal jest zaangażowane. Problem podstawowy nadal występuje w Windows PowerShell v5.1 / PowerShell Core 6.2.0-Preview.1 - zobacz ten raport o błędzie .
mklement
3

Boże Wiele odpowiedzi. Szczerze wolę ten od nich wszystkich. Jest bardzo prosty, kompletny, czytelny i działa na dowolnym komputerze z systemem Windows. Wykorzystuje (niezawodną) funkcję kasowania rekurencyjnego .NET, a jeśli z jakiegoś powodu zawiedzie, generuje odpowiedni wyjątek, który można obsłużyć za pomocą bloku try / catch.

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

Należy pamiętać, że Resolve-Pathwiersz jest ważny, ponieważ .NET nie rozpoznaje bieżącego katalogu podczas rozwiązywania względnych ścieżek plików. To jedyna myśl, o której mogę myśleć.

Phil
źródło
2

Oto co mam do pracy:

$Target = "c:\folder_to_delete"

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

Remove-Item -Recurse -Force $Target

Ten pierwszy wiersz usuwa wszystkie pliki z drzewa. Drugi usuwa wszystkie foldery, w tym górny.

James Copeland
źródło
Chociaż może to zmniejszyć prawdopodobieństwo niepowodzenia, może ono nadal zawieść, biorąc pod uwagę, że Remove-Item -Recursenadal jest zaangażowane. Problem podstawowy nadal występuje w Windows PowerShell v5.1 / PowerShell Core 6.2.0-Preview.1 - zobacz ten raport o błędzie .
mklement
0

Miałem ten problem z katalogiem, którego nie można usunąć. Odkryłem, że jeden z podfolderów był uszkodzony i kiedy próbowałem przenieść lub zmienić nazwę tego dziecięcego katalogu, dostałem komunikat o błędzie informujący o tym, że go brakuje. Próbowałem użyć rm -Force i dostałem ten sam błąd co ty.

Dla mnie zadziałało skompresowanie katalogu nadrzędnego za pomocą 7-zip z zaznaczoną opcją „Usuń pliki po kompresji”. Po skompresowaniu udało mi się usunąć plik zip.

RedDawnRising
źródło