La rimozione forzata di file e directory in PowerShell ha esito negativo a volte, ma non sempre


33

Sto cercando di eliminare una directory in modo ricorsivo rm -Force -Recurse somedirectory, ricevo diversi errori "La directory non è vuota". Se ritento lo stesso comando , ha esito positivo.

Esempio:

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>

Certo, questo non succede sempre . Inoltre, non succede solo con le _svndirectory e non ho una cache TortoiseSVN o qualcosa del genere, quindi nulla blocca la directory.

Qualche idea?

Risposte:


31

help Remove-Item dice:

Il parametro Recurse in questo cmdlet non funziona correttamente.

e

Poiché il parametro Recurse in questo cmdlet è errato, il comando utilizza il cmdlet Get-Childitem per ottenere i file d desiderati e utilizza l'operatore pipeline per passarli al cmdlet Remove-Item.

e propone questa alternativa come esempio:

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

Quindi dovresti get-childitem -recurseentrare remove-item.


Grazie. Ho appena trovato questo thread dal 2006: vistax64.com/powershell/… sembra che Microsoft non sia davvero interessata a risolvere questo problema.
Mauricio Scheffer,

@mausch: vedi questo riferimento più recente, ma ancora irrisolto: Rimuovi-Articolo -Recorso
In pausa fino a ulteriore avviso.

se fai un attraversamento ed elimini, dovrai prima attraversare le directory secondarie e prima i loro file.
fschwiet,

2
Almeno la documentazione dice che non funziona.
Derekerdmann,

6
Ho dovuto mettere entrambi i flag -force -recurse per Remove-Item, altrimenti mi ha spinto "per favore conferma" Get-ChildItem -Path $ Destination -Recurse | Remove-Item -force -recurse
MiFreidgeim SO-smetti di essere malvagio il

17

@JamesCW: il problema persiste in PowerShell 4.0

Ho provato un'altra soluzione alternativa e ha funzionato: utilizzare cmd.exe:

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

1
Buon vecchio rd / s / q!
JamesCW,

Ho provato ogni variante di Get-ChildItem; riprovare loop; chiamare iisresetprima dell'eliminazione e nulla sembra funzionare in modo affidabile . Proverò questo, anche se quando l'ho visto per la prima volta mi sono rifiutato di avere DOS nel mio Powershell ...
Peter McEvoy,

Sfortunatamente, rd /sfallisce anche a intermittenza (anche se apparentemente meno spesso di Remove-Item): github.com/Microsoft/console/issues/309
mklement

Non mi piace la barra della c per me. Devi precederlo con il comando powershell e citare una sola parte la parte cmd.exe? Ottengo "È necessario fornire un'espressione di valore seguendo l'operatore '/'." "Token inatteso 'c' nell'espressione o nell'affermazione. È lo stesso con il comando PowerShell di fronte ad esso. Il / deve scappare?
Michele,

7

ETA 20181217: PSVersion 4.0 e successive falliranno comunque in alcune circostanze, vedi la risposta alternativa di Mehrdad Mirreza e la segnalazione di bug presentata da mklement

mklement fornisce una soluzione Proof of Concept a questa risposta SO , poiché il bug è in attesa di una correzione ufficiale

La nuova versione di PowerShell( PSVersion 4.0) ha risolto completamente questo problema e Remove-Item "targetdirectory" -Recurse -Forcefunziona senza problemi di temporizzazione.

È possibile verificare la versione eseguendo $PSVersiontabledall'ISE o dal PowerShellprompt. 4.0 è la versione fornita con Windows 8.1e Server 2012 R2e può essere installata anche su versioni precedenti di Windows.


5
Si verifica ancora per me in PowerShell 4.0
ajbeaven

10
Si verifica ancora in PowerShell v5 !!!!! 11 !! 1! 1 !!!
Richard Hauer,

@RichardHauer ora sono solo confuso
JamesCW il

2
@JamesCW Ho convertito alla rdversione. Oltre a funzionare davvero, è circa 3 volte più veloce
Richard Hauer il

Il problema non è stato risolto a partire da Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - vedere questo report di bug . Anche se rd /spuò fallire meno spesso, anche questo è rotto - vedi questo report di bug .
mklement

4

Aggiornamento : apparentemente ci sono piani per rendere sincrone le API di rimozione degli elementi del filesystem di Windows, ma non sono ancora sincrone a partire dalla versione di Windows 10 1903 - vedi questo commento su GitHub .


Le risposte esistenti mitigano il problema, in modo che si verifichi meno frequentemente, ma non affrontano la causa principale , motivo per cui possono ancora verificarsi errori.

Remove-Item -Recurseè inaspettatamente asincrono , in definitiva perché i metodi dell'API di Windows per la rimozione di file e directory sono intrinsecamente asincroni e Remove-Itemnon tengono conto di ciò.

Ciò si manifesta in modo intermittente e imprevedibile in due modi:

  • Il tuo caso: la rimozione di una directory non vuota stessa può non riuscire, se la rimozione di una sottodirectory o di un file in essa non è stata ancora completata al momento in cui viene effettuato un tentativo di rimozione della directory principale.

  • Meno comunemente: la ricostruzione di una directory rimossa immediatamente dopo la rimozione potrebbe non riuscire, poiché la rimozione potrebbe non essere stata ancora completata al momento del tentativo di ricostruzione.

Il problema non riguarda solo PowerShell di Remove-Item, ma anche cmd.exe's rd /scosì come .NET di[System.IO.Directory]::Delete() :

A partire da 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, Remove-Item, né rd /s, né [System.IO.Directory]::Delete()funzionano in modo affidabile , poiché non riescono a rendere conto dell'asincrono comportamento delle funzioni di rimozione file / directory dell'API di Windows :

Per una funzione PowerShell personalizzata che fornisce una soluzione sincrona affidabile , vedere questa risposta SO .


Quando si gestiscono i file in cui la rimozione è certa:while($true) { if ( (Remove-Item [...] *>&1) -ne $null) { Start-Sleep 0.5 } else { break } }
Farway

3

La risposta attuale non cancellerà effettivamente una directory, ma solo i suoi figli. Inoltre avrà problemi con le directory nidificate poiché tenterà nuovamente di eliminare una directory prima del suo contenuto. Ho scritto qualcosa per eliminare i file nell'ordine corretto, avrebbe comunque lo stesso problema anche se a volte la directory sarebbe ancora in giro in seguito.

Quindi, ora uso qualcosa che rileverà l'eccezione, attenderò e riproverò (3 volte):

Per ora sto usando questo:

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
Questo è buono ma ho ancora avuto problemi con esso. Se il comando mkdir viene eseguito prima che il sistema completi il ​​comando rm, può lanciare un System.UnauthorizedAccessException con un FullQualifiedErrorId di ItemExistsUnauthorizedAccessError. Cioè, la directory non è stata ancora cancellata dal sistema operativo (sul mio HDD lento). Quindi anche quell'errore deve essere colto. Ed è un errore non terminante, quindi ErrorAction deve essere impostato su Stop. Ho anche inserito il comando rm nel blocco try, solo in caso di errori transitori di I / O durante l'eliminazione.
Mark Lapierre,

Non riesco a credere che questo debba essere fatto. Accidenti, Powershell fa schifo!
jcollum,

3

Per eliminare la directory e il suo contenuto sono necessari due passaggi. Prima elimina il contenuto, quindi la cartella stessa. Utilizzando la soluzione alternativa per l'elemento di rimozione ricorsivo difettoso, la soluzione sarebbe simile alla seguente:

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

In questo modo è possibile rimuovere anche la directory principale.


1
Questo è esattamente ciò che diceva la risposta accettata. Hai qualcosa da aggiungere?
Michael Hampton

1
Stanno sottolineando che la risposta accettata non cancella la directory stessa, quindi richiede due passaggi.
Paul George,

2
Il Remove-Itemcomando in cui viene eseguito il piping presenta lo stesso problema indicato in origine. Potrebbe inciampare su un elemento della directory che non è vuoto allo stesso modo.
Dejan,

@Dejan Questa directory non potrebbe essere ancora vuota se la prima riga di questo codice funzionasse, vero?
Ifedi Okonkwo,

1
Mentre questo può ridurre la probabilità di fallimento, può comunque fallire, dato che Remove-Item -Recurseè ancora coinvolto. Il problema di fondo esiste ancora a partire da Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - vedere questo report di bug .
mklement

3

Perbacco. Molte risposte. Onestamente preferisco questo a tutti loro. È super semplice, completo, leggibile e funziona su qualsiasi macchina Windows. Utilizza la funzionalità di eliminazione ricorsiva (affidabile) di .NET e se non riesce per qualche motivo, genera un'eccezione adeguata che può essere gestita con un blocco try / catch.

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

Si noti che la Resolve-Pathlinea è importante perché .NET non è a conoscenza della directory corrente durante la risoluzione dei percorsi di file relativi. Questo è l'unico gotcha che mi viene in mente.


2

Questo è quello che sto lavorando:

$Target = "c:\folder_to_delete"

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

Remove-Item -Recurse -Force $Target

Questa prima riga elimina tutti i file nella struttura. Il secondo elimina tutte le cartelle, inclusa la parte superiore.


Mentre questo può ridurre la probabilità di fallimento, può comunque fallire, dato che Remove-Item -Recurseè ancora coinvolto. Il problema di fondo esiste ancora a partire da Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - vedere questo report di bug .
mklement


0

Ho avuto questo problema con una directory che non avrebbe eliminato. Ho scoperto che una delle sottocartelle era corrotta e quando ho provato a spostare o rinominare quella directory figlio ho ricevuto un messaggio di errore che diceva che mancava qualcosa. Ho provato a usare rm -Force e ho avuto lo stesso errore che hai fatto tu.

Quello che ha funzionato per me è stato comprimere la directory principale usando 7-zip con l'opzione "Elimina file dopo la compressione" selezionata. Una volta compresso sono stato in grado di eliminare il file zip.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.