web-dev-qa-db-fra.com

La suppression forcée de fichiers et de répertoires dans PowerShell échoue parfois, mais pas toujours

J'essaie de supprimer un répertoire récursivement avec rm -Force -Recurse somedirectory, J'obtiens plusieurs erreurs "Le répertoire n'est pas vide". Si je réessaye la même commande , cela réussit.

Exemple:

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>

Bien sûr, cela ne se produit pas toujours . En outre, cela ne se produit pas uniquement avec _svn répertoires, et je n'ai pas de cache TortoiseSVN ou quelque chose comme ça donc rien ne bloque le répertoire.

Des idées?

34

help Remove-Item dit:

Le paramètre Recurse de cette applet de commande ne fonctionne pas correctement.

et

Étant donné que le paramètre Recurse de cette applet de commande est défectueux, la commande utilise l'applet de commande Get-Childitem pour obtenir les fichiers désirés d et utilise l'opérateur de pipeline pour les transmettre à l'applet de commande Remove-Item.

et propose cette alternative comme exemple:

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

Vous devez donc diriger get-childitem -recurse en remove-item.

@JamesCW: Le problème existe toujours dans PowerShell 4.0

J'ai essayé une autre solution de contournement et cela a fonctionné: utilisez cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
19
Mehrdad Mirreza

ETA 20181217: PSVersion 4.0 et versions ultérieures échoueront toujours dans certaines circonstances, voir la réponse alternative par Mehrdad Mirreza , et rapport de bogue classé par - mklement

mklement fournit une solution Proof of Concept à ce sujet réponse SO , car le bogue attend une correction officielle

La nouvelle version de PowerShell (PSVersion 4.0) a entièrement résolu ce problème et Remove-Item "targetdirectory" -Recurse -Force fonctionne sans aucun problème de synchronisation.

Vous pouvez vérifier votre version en exécutant $PSVersiontable depuis l'ISE ou PowerShell Invite. 4.0 est la version fournie avec Windows 8.1 et Server 2012 R2, et il peut également être installé sur les versions précédentes de Windows.

8
JamesCW

Update: Apparemment, il existe plans pour rendre les API de suppression des éléments du système de fichiers Windows synchrones, mais elles ne le sont pas encore à partir de Windows 10 version 1903 - voir ce commentaire sur GitHub .


Les réponses existantes atténuer le problème, de sorte qu'il se produit moins fréquemment, mais elles n'abordent pas la cause première, c'est pourquoi des échecs peuvent toujours se produire.

Remove-Item -Recurse Est inopinément asynchrone, finalement parce que le Windows Les méthodes d'API pour la suppression de fichiers et de répertoires sont intrinsèquement asynchrones et Remove-Item Ne tient pas compte de cela.

Ceci par intermittence, de façon imprévisible se manifeste de deux manières:

  • Votre cas: la suppression d'un répertoire non vide lui-même peut échouer si la suppression d'un sous-répertoire ou d'un fichier qu'il contient n'est pas terminée au moment où une tentative est effectuée pour supprimer le répertoire parent.

  • Plus rarement: la recréation d'un répertoire supprimé immédiatement après la suppression peut échouer, car la suppression n'est peut-être pas encore terminée au moment où la recréation est tentée.

Le problème affecte non seulement Remove-Item De PowerShell, mais aussi cmd.exe De rd /s Ainsi que [System.IO.Directory]::Delete() de .NET :

À partir de Windows PowerShell v5.1/PowerShell Core 6.2.0-preview.1/cmd.exe 10.0.17134.407/.NET Framework 4.7.03056, .NET Core 2.1, ni Remove-Item, Ni rd /s, Ni [System.IO.Directory]::Delete() fonctionnent de manière fiable , car ils ne tiennent pas compte de le comportement asynchrone des fonctions de suppression de fichiers/répertoires de l'API Windows :

Pour une fonction PowerShell personnalisée qui fournit une solution de contournement synchrone fiable , voir - cette réponse SO .

7
mklement

Pour supprimer le répertoire et son contenu, deux étapes sont nécessaires. Supprimez d'abord le contenu, puis le dossier lui-même. En utilisant la solution de contournement pour l'élément de suppression récursif défectueux, la solution ressemblerait à ceci:

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

De cette façon, vous pouvez également supprimer le répertoire parent.

3
Carl Baker

La réponse actuelle ne supprimera pas réellement un répertoire, juste ses enfants. De plus, il rencontrera des problèmes avec les répertoires imbriqués car il essaiera à nouveau de supprimer un répertoire avant son contenu. J'ai écrit quelque chose pour supprimer les fichiers dans le bon ordre, aurait toujours le même problème bien que parfois le répertoire soit toujours là après.

Donc, maintenant j'utilise quelque chose qui interceptera l'exception, attendra et réessayera (3 fois):

Pour l'instant, j'utilise ceci:

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)
    }
}
3
fschwiet

Mon Dieu. Beaucoup de réponses. Honnêtement, je préfère celui-ci à chacun d'eux. C'est super simple, complet, lisible et fonctionne sur n'importe quelle machine Windows. Il utilise la fonctionnalité de suppression récursive (fiable) de .NET et s'il échoue pour une raison quelconque, il lève une exception appropriée qui peut être gérée avec un bloc try/catch.

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

Notez que le Resolve-Path la ligne est importante car .NET n'est pas au courant de votre répertoire actuel lors de la résolution des chemins de fichiers relatifs. C'est à peu près le seul problème auquel je peux penser.

3
Phil

Voici ce que je travaille:

$Target = "c:\folder_to_delete"

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

Remove-Item -Recurse -Force $Target

Cette première ligne supprime tous les fichiers de l'arborescence. Le second supprime tous les dossiers, y compris le haut.

2
James Copeland

Prenez d'abord possession des fichiers/répertoires à l'aide de Takeown.exe, puis supprimez

https://learn-powershell.net/2014/06/24/changing-ownership-of-file-or-folder-using-powershell/

0
AveryFreeman

J'ai eu ce problème avec un répertoire qui ne serait pas supprimé. J'ai constaté que l'un des sous-dossiers était corrompu et lorsque j'ai essayé de déplacer ou de renommer ce répertoire enfant, j'ai reçu un message d'erreur indiquant qu'il manquait quelque chose. J'ai essayé d'utiliser rm -Force et j'ai eu la même erreur que vous.

Ce qui a fonctionné pour moi a été de compresser le répertoire parent à l'aide de 7-Zip avec l'option "Supprimer les fichiers après compression" cochée. Une fois compressé, j'ai pu supprimer le fichier Zip.

0
RedDawnRising