Das Erzwingen des Entfernens von Dateien und Verzeichnissen in PowerShell schlägt manchmal, aber nicht immer fehl

33

Ich versuche ein Verzeichnis rekursiv mit zu löschen rm -Force -Recurse somedirectory, es werden mehrere Fehler "Das Verzeichnis ist nicht leer" angezeigt. Wenn ich denselben Befehl erneut versuche , ist er erfolgreich.

Beispiel:

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>

Das passiert natürlich nicht immer . Es kommt auch nicht nur bei _svnVerzeichnissen vor, und ich habe keinen TortoiseSVN- Cache oder ähnliches, sodass das Verzeichnis nicht blockiert wird.

Irgendwelche Ideen?

Mauricio Scheffer
quelle

Antworten:

31

help Remove-Item sagt:

Der Recurse-Parameter in diesem Cmdlet funktioniert nicht ordnungsgemäß.

und

Da der Recurse-Parameter in diesem Cmdlet fehlerhaft ist, verwendet der Befehl das Cmdlet Get-Childitem, um die gewünschten d-Dateien abzurufen, und verwendet den Pipelineoperator, um sie an das Cmdlet Remove-Item zu übergeben.

und schlägt diese Alternative als Beispiel vor:

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

So sollten Sie Rohr get-childitem -recursein remove-item.

Bis auf weiteres angehalten.
quelle
Vielen Dank. Ich habe gerade diesen Thread aus dem Jahr 2006 gefunden: vistax64.com/powershell/… Microsoft scheint nicht wirklich daran interessiert zu sein, diesen Fehler zu beheben.
Mauricio Scheffer
@mausch: Siehe hierzu den neueren, aber noch ungelösten Verweis: Remove-Item -Recurse
Bis auf weiteres angehalten.
Wenn Sie einen Durchlauf durchführen und löschen, müssen Sie zuerst die untergeordneten Verzeichnisse und deren Dateien durchlaufen.
Schwiet
2
Zumindest heißt es in der Dokumentation, dass es nicht funktioniert.
Derekerdmann
6
Ich musste beide -force -recurse-Flags für Remove-Item setzen, andernfalls wurde ich aufgefordert "Bitte bestätigen" Get-ChildItem -Path $ Destination -Recurse | Remove-Item -force -recurse
MiFreidgeim SO-aufhören, böse zu sein
17

@ JamesCW: Das Problem besteht weiterhin in PowerShell 4.0

Ich habe eine andere Problemumgehung versucht und es hat funktioniert: Verwenden Sie cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
Mehrdad Mirreza
quelle
1
Gute alte rd / s / q!
JamesCW
Ich habe jede Variante von Get-ChildItem ausprobiert. Schleifen wiederholen; iisresetVor dem Löschen anrufen und nichts scheint zuverlässig zu funktionieren . Ich werde es versuchen, obwohl ich, als ich es das erste Mal sah, keine DOS in meiner Powershell haben wollte ...
Peter McEvoy
rd /sRemove-Item
Scheitert
Mir gefällt der Schrägstrich durch das c nicht. Müssen Sie ihm einen Powershell-Befehl voranstellen und den cmd.exe-Teil in einfache Anführungszeichen setzen? Ich erhalte die Meldung "Sie müssen nach dem Operator '/' einen Werteausdruck eingeben." "Unerwartetes Token 'c' in Ausdruck oder Anweisung. Dasselbe gilt für den Powershell-Befehl davor. Muss der / die entkommen?
Michele
7

ETA 20181217: PSVersion 4.0 und höher schlägt unter bestimmten Umständen immer noch fehl, siehe alternative Antwort von Mehrdad Mirreza und Fehlerbericht von mklement

mklement bietet eine Proof of Concept-Lösung für diese SO-Antwort , da der Fehler auf eine offizielle Korrektur wartet

Die neue Version von PowerShell( PSVersion 4.0) hat dieses Problem vollständig behoben und Remove-Item "targetdirectory" -Recurse -Forcefunktioniert ohne Zeitprobleme.

Sie können Ihre Version überprüfen, indem Sie sie $PSVersiontablein der ISE oder in der PowerShellEingabeaufforderung ausführen. 4.0 ist die mit Windows 8.1und gelieferte Version Server 2012 R2und kann auch auf früheren Windows-Versionen installiert werden.

JamesCW
quelle
5
Tritt
10
Kommt immer noch in PowerShell v5 vor !!!!! 11 !! 1! 1 !!!
Richard Hauer
@RichardHauer Nun, ich bin nur verwirrt
JamesCW
2
@JamesCW Ich habe auf die rdVersion konvertiert . Abgesehen von der eigentlichen Arbeit ist es etwa 3x schneller
Richard Hauer
Das Problem ist nicht sehen - als von Windows Powershell v5.1 / Powershell - Core 6.2.0-preview.1 fixiert diesen Fehler melden . Es rd /skann zwar seltener scheitern, ist aber auch kaputt - siehe diesen Fehlerbericht .
Mklement
4

Update : Scheinbar ist geplant , die APIs zum Entfernen von Windows-Dateisystemelementen zu synchronisieren, diese sind jedoch ab Windows 10, Version 1903, noch nicht synchron - siehe diesen Kommentar zu GitHub .


Die vorhandenen Antworten mindern das Problem, sodass es weniger häufig auftritt, aber die eigentliche Ursache nicht behoben wird. Aus diesem Grund können weiterhin Fehler auftreten.

Remove-Item -Recurseist unerwartet asynchron , da die Windows-API-Methoden zum Entfernen von Dateien und Verzeichnissen inhärent asynchron sind und Remove-Itemdies nicht berücksichtigen.

Dies äußert sich zeitweise und unvorhersehbar auf zwei Arten:

  • Ihr Fall: Das Entfernen eines nicht leeren Verzeichnisses selbst kann fehlschlagen, wenn das Entfernen eines Unterverzeichnisses oder einer Datei darin zum Zeitpunkt des Versuchs, das übergeordnete Verzeichnis zu entfernen, noch nicht abgeschlossen ist.

  • Weniger häufig: Die Neuerstellung eines entfernten Verzeichnisses unmittelbar nach dem Entfernen kann fehlschlagen, da das Entfernen zum Zeitpunkt der Neuerstellung möglicherweise noch nicht abgeschlossen ist.

Das Problem betrifft nicht nur die Powershell Remove-Item, sondern auch cmd.exe‚s rd /sals auch von .NET[System.IO.Directory]::Delete() :

Ab 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, weder Remove-Item, noch rd /s, noch [System.IO.Directory]::Delete()zuverlässig arbeiten , weil sie für die asynchron Konto fehlschlagen Verhalten der Windows-API-Funktionen zum Entfernen von Dateien / Verzeichnissen :

Eine benutzerdefinierte PowerShell-Funktion , die eine zuverlässige synchrone Problemumgehung bietet , finden Sie in dieser SO-Antwort .

mklement
quelle
Beim Umgang mit Dateien, bei denen das Entfernen sicher ist:while($true) { if ( (Remove-Item [...] *>&1) -ne $null) { Start-Sleep 0.5 } else { break } }
Farway
3

Die aktuelle Antwort löscht kein Verzeichnis, sondern nur die untergeordneten Verzeichnisse. Außerdem wird es Probleme mit verschachtelten Verzeichnissen geben, da erneut versucht wird, ein Verzeichnis vor dessen Inhalt zu löschen. Ich habe etwas geschrieben, um die Dateien in der richtigen Reihenfolge zu löschen, hätte aber immer noch das gleiche Problem, obwohl das Verzeichnis manchmal auch danach noch vorhanden wäre.

Also benutze ich jetzt etwas, das die Ausnahme abfängt, warte und versuche es erneut (3 Mal):

Im Moment benutze ich das:

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
quelle
1
Das ist gut, aber ich hatte immer noch Probleme damit. Wenn der Befehl mkdir ausgeführt wird, bevor das System den Befehl rm ausgeführt hat, kann eine System.UnauthorizedAccessException mit einer FullyQualifiedErrorId von ItemExistsUnauthorizedAccessError ausgelöst werden. Dh das Verzeichnis wurde vom Betriebssystem noch nicht gelöscht (auf meiner langsamen Festplatte). Also muss dieser Fehler auch abgefangen werden. Und es ist ein nicht beendender Fehler, daher muss die ErrorAction auf Stop gesetzt werden. Ich habe auch den Befehl rm in den try-Block eingefügt, nur in Fällen, in denen beim Löschen vorübergehende E / A-Fehler auftreten.
Mark Lapierre
Ich kann nicht glauben, dass das überhaupt getan werden muss. Verdammt, Powershell ist scheiße!
jcollum
3

Das Löschen des Verzeichnisses und seines Inhalts erfolgt in zwei Schritten. Löschen Sie zuerst den Inhalt, dann den Ordner selbst. Wenn Sie die Problemumgehung für das fehlerhafte rekursive Element remove verwenden, sieht die Lösung folgendermaßen aus:

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

Auf diese Weise können Sie auch das übergeordnete Verzeichnis entfernen.

Carl Baker
quelle
1
Dies ist genau das, was die akzeptierte Antwort sagte. Haben Sie noch etwas hinzuzufügen?
Michael Hampton
1
Sie weisen darauf hin, dass die akzeptierte Antwort das Verzeichnis selbst nicht löscht, daher sind zwei Schritte erforderlich.
Paul George
2
Der Remove-ItemBefehl, an den Ihre Leitung weitergeleitet wird, hat dasselbe Problem wie ursprünglich angegeben. Es könnte auf ein Verzeichniselement stoßen, das auf die gleiche Weise nicht leer ist.
Dejan
@Dejan Dieses Verzeichnis könnte nicht noch leer sein, wenn die erste Zeile dieses Codes funktioniert, oder?
Ifedi Okonkwo
1
Während dies die Wahrscheinlichkeit eines Ausfalls verringern kann, kann es dennoch ausfallen, vorausgesetzt, dies Remove-Item -Recurseist immer noch involviert. Das zugrunde liegende Problem besteht ab Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 weiterhin - siehe diesen Fehlerbericht .
Mklement
3

Meine Güte. Viele Antworten. Ich ziehe ehrlich gesagt diesen allen vor. Es ist super einfach, vollständig, lesbar und funktioniert auf jedem Windows-Computer. Es verwendet die (verlässliche) rekursive Löschfunktion von .NET und löst, falls dies aus irgendeinem Grund fehlschlägt, eine richtige Ausnahme aus, die mit einem try / catch-Block behandelt werden kann.

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

Beachten Sie, dass die Resolve-PathZeile wichtig ist, da .NET Ihr aktuelles Verzeichnis beim Auflösen relativer Dateipfade nicht kennt. Das ist ungefähr das einzige, was mir einfällt.

Phil
quelle
2

Das ist, woran ich arbeite:

$Target = "c:\folder_to_delete"

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

Remove-Item -Recurse -Force $Target

Diese erste Zeile löscht alle Dateien im Baum. Die zweite löscht alle Ordner einschließlich der obersten.

James Copeland
quelle
Während dies die Wahrscheinlichkeit eines Ausfalls verringern kann, kann es dennoch ausfallen, vorausgesetzt, dies Remove-Item -Recurseist immer noch involviert. Das zugrunde liegende Problem besteht ab Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 weiterhin - siehe diesen Fehlerbericht .
Mklement
0

Ich hatte dieses Problem mit einem Verzeichnis, das nicht gelöscht werden konnte. Ich habe festgestellt, dass einer der Unterordner beschädigt ist, und als ich versuchte, das untergeordnete Verzeichnis zu verschieben oder umzubenennen, wurde eine Fehlermeldung angezeigt, die besagt, dass etwas fehlt. Ich habe versucht, rm -Force zu verwenden und habe den gleichen Fehler wie Sie erhalten.

Bei mir funktionierte das Komprimieren des übergeordneten Verzeichnisses mit 7-zip, wobei die Option "Dateien nach Komprimierung löschen" aktiviert war. Sobald es komprimiert wurde, konnte ich die Zip-Datei löschen.

RedDawnRising
quelle