Примусово видаляти файли та каталоги в 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 в цьому командлеті не працює належним чином.

і

Оскільки параметр Recurse в цьому командлеті несправний, команда використовує командлет Get-Childitem для отримання файлів бажання d, і він використовує оператор конвеєра, щоб передати їх командлету Remove-Item.

і пропонує цю альтернативу як приклад:

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

Таким чином , ви повинні трубу get-childitem -recurseв remove-item.


Спасибі. Щойно знайшов цю тему з 2006 року: vistax64.com/powershell/… схоже, Microsoft не дуже зацікавлений у виправленні цього.
Маурісіо Шеффер

@mausch: Перегляньте це нещодавнє, але все ще невирішене посилання: Remove-Item -Recurse
Призупинено до подальшого повідомлення.

якщо ви робите обхід і видаляєте, вам доведеться спочатку перейти дочірні каталоги та спочатку їх файли.
fschwiet

2
Принаймні в документації написано, що вона не працює.
derekerdmann

6
Мені довелося поставити обидва прапорця -force -recurse для Remove-Item, інакше це наштовхнуло на мене "підтвердьте" Get-ChildItem -Path $ Destination -Recurse | Remove-Item -force -recurse
MiFreidgeim SO-перестань бути злим

17

@JamesCW: Проблема все ще існує в PowerShell 4.0

Я спробував інше рішення, і воно спрацювало: use cmd.exe:

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

1
Добрий старий rd / s / q!
JamesCW

Я спробував усі варіанти Get-ChildItem; повторні петлі; дзвінок iisresetперед видаленням, і, здається, нічого не працює надійно . Я спробую це, навіть коли вперше побачив це, я заграв, що мав DOS в моєму Powershell ...
Пітер МакЕвой

На жаль, rd /sтеж невдало (хоча здається рідше Remove-Item): github.com/Microsoft/console/isissue/309
mklement

Мені це не подобається нахил на c. Вам потрібно передувати це посиланням-command та однозначною цитатою частини cmd.exe? Я отримую "Ви повинні надати значення виразу після оператора '/'." "Несподіваний маркер" c "у виразі чи вислові. Це те ж саме з повноваженнями-командою перед ним. Чи / потрібно втекти?
Мішель

7

ETA 20181217: PSVersion 4.0 і пізніші версії все ще не спрацюють за певних обставин, див. Альтернативну відповідь Мехрдада Міррези та звіт про помилки, поданий mklement

mklement забезпечує рішення Proof of Concept на цю відповідь ТА , оскільки помилка чекає офіційного виправлення

Нова версія 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 !!!
Річард Хауер

@RichardHauer добре зараз я просто заплутався
JamesCW

2
@JamesCW Я перейшов у rdверсію. Окрім того, що насправді працює, це приблизно в 3 рази швидше
Річард Хауер

Проблема не була виправлена ​​у Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - див. Цей звіт про помилку . Хоча rd /sможе виходити з ладу рідше, він також порушений - див. Цей звіт про помилку .
mklement

4

Оновлення : начебто є плани зробити API видалення елементів файлової системи Windows синхронним, але вони ще не синхронні як для Windows 10 версії 1903 - дивіться цей коментар на GitHub .


Існуючі відповіді пом'якшують проблему, так що вона трапляється рідше, але вони не стосуються першопричини , через що все ж можуть виникати збої.

Remove-Item -Recurseнесподівано асинхронний , зрештою, тому що методи API для видалення файлів і каталогів за своєю суттю є асинхронними і Remove-Itemне враховують цього.

Це періодично, непередбачувано проявляється одним із двох способів:

  • Ваш випадок: Видалення непорожнього каталогу сам по собі може не вдатися, якщо видалення підкаталогу або файлу з нього ще не завершилось до моменту спроби видалити батьківський каталог.

  • Рідше: відтворення видаленого каталогу відразу після видалення може не вдатися, оскільки видалення ще не завершилось до моменту спроби відновлення.

Проблема зачіпає не тільки PowerShell - х Remove-Item, але і cmd.exe«S rd /s, а також .NET - х[System.IO.Directory]::Delete() :

Станом на Windows PowerShell v5.1 / PowerShell Core 6.2.0-превью.1 / cmd.exe10.0.17134.407 / .NET Framework 4.7.03056, .NET Core 2.1, ні Remove-Item, ні rd /s, ні [System.IO.Directory]::Delete()надійно працювати , оскільки вони не враховують асинхронність поведінка файлів / функцій видалення файлу API / API :

Про власну функцію PowerShell, яка забезпечує надійно синхронне вирішення , дивіться цю відповідь ТА .


При обробці файлів, де видалення визначено: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. Тобто, каталог ще не видалено ОС (на моєму повільному жорсткому диску). Тож і цю помилку потрібно вловлювати. І це непостійна помилка, тому ErrorAction потрібно встановити на Стоп. Я також поміщаю команду rm у блок спробу, лише у випадках, коли при видаленні є тимчасові помилки вводу-виводу.
Марк Лап'єр

Я не можу повірити, що це навіть потрібно зробити. Чорт, Powershell смокче!
jcollum

3

Щоб видалити каталог та його вміст, потрібно два кроки. Спочатку видаліть вміст, потім саму папку. Використовуючи вирішення для несправного рекурсивного елемента видалення, рішення виглядатиме так:

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

Таким чином ви також можете видалити батьківський каталог.


1
Саме так сказана прийнята відповідь. Вам є що додати?
Майкл Хемптон

1
Вони вказують, що прийнята відповідь не видаляє саму директорію, отже, це робить два кроки.
Пол Джордж

2
Remove-ItemКоманду вашого трубопроводу в та ж проблема , що спочатку йшлося. Він може натрапити на елемент каталогу, який не є порожнім таким же чином.
Деян

@Dejan Цей каталог ще не міг бути порожнім, якби перший рядок цього коду працював, чи не так?
Іфдіді Оконкво

1
Незважаючи на те, що це може зменшити ймовірність невдачі, вона все ще може зазнати невдачі, враховуючи, що Remove-Item -Recurseце все ще є. Основна проблема все ще існує як у Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - дивіться цей звіт про помилку .
mklement

3

Шляхти. Багато відповідей. Я чесно віддаю перевагу цьому над усіма ними. Це дуже просто, повно, читабельно і працює на будь-якій машині Windows. Він використовує (надійний) рекурсивний функціонал видалення .NET, і якщо з певних причин виходить з ладу, він видає належне виключення, яке можна обробити блоком спробу / лову.

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

Зауважте, що Resolve-Pathрядок є важливим, оскільки .NET не знає про ваш поточний каталог під час вирішення відносних шляхів до файлів. Це про єдину готчу, про яку я можу придумати.


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 і отримав таку ж помилку, що і ви.

Що для мене спрацювало - стиснення батьківського режиму за допомогою 7-zip з перевіркою параметра "Видалити файли після стиснення". Після його стиснення я зміг видалити zip-файл.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.