Назад | Перейти на главную страницу

Принудительное удаление файлов и каталогов в PowerShell иногда дает сбой, но не всегда

Я пытаюсь рекурсивно удалить каталог с помощью 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 cache или что-то в этом роде, поэтому каталог не блокируется.

Любые идеи?

help Remove-Item говорит:

Параметр Recurse в этом командлете не работает должным образом.

и

Поскольку параметр Recurse в этом командлете неисправен, команда использует командлет Get-Childitem для получения желаемых файлов d и использует оператор конвейера для передачи их в командлет Remove-Item.

и предлагает эту альтернативу в качестве примера:

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

Итак, вы должны трубить get-childitem -recurse в remove-item.

@JamesCW: проблема все еще существует в PowerShell 4.0

Я попробовал другой обходной путь, и он сработал: используйте cmd.exe:

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

ETA 20181217: PSVersion 4.0 и более поздние версии по-прежнему не работают при некоторых обстоятельствах, см. Альтернативный ответ от Мехрдад Мирреза, и отчет об ошибке поданный мклемент

мклемент предоставляет решение Proof of Concept на этом ТАК ответ, поскольку ошибка ожидает официального исправления

Новая версия PowerShell (PSVersion 4.0) полностью решил эту проблему и Remove-Item "targetdirectory" -Recurse -Force работает без проблем с синхронизацией.

Вы можете проверить свою версию, запустив $PSVersiontable изнутри ISE или PowerShell Подсказка. 4.0 - это версия, которая поставляется с Windows 8.1 и Server 2012 R2, и его также можно установить в предыдущих версиях Windows.

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


Существующие ответы смягчать проблема, поэтому она возникает реже, но они не решают основная причина, поэтому сбои все еще могут происходить.

Remove-Item -Recurse неожиданно асинхронный, в конечном итоге потому, что Методы Windows API для удаления файлов и каталогов по своей сути асинхронны. и Remove-Item не учитывает это.

это периодически, непредсказуемо проявляется одним из двух способов:

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

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

В проблема затрагивает не только PowerShell Remove-Item, но также cmd.exeс rd /s а также .NET [System.IO.Directory]::Delete():

Начиная с 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, ни то, ни другое Remove-Item, ни rd /s, ни [System.IO.Directory]::Delete() работать надежно, потому что они не учитывать асинхронное поведение функций удаления файлов / каталогов Windows API:

Для настраиваемая функция PowerShell что обеспечивает надежно синхронный обходной путь, видеть этот ТАК ответ.

Текущий ответ на самом деле не удаляет каталог, а только его дочерние элементы. Кроме того, у него будут проблемы с вложенными каталогами, так как он снова будет пытаться удалить каталог до его содержимого. Я написал что-то, чтобы удалить файлы в правильном порядке, все равно будет та же проблема, хотя иногда каталог все еще будет после этого.

Итак, теперь я использую что-то, что будет ловить исключение, ждать и повторять (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)
    }
}

Чтобы удалить каталог и его содержимое, нужно выполнить два шага. Сначала удалите содержимое, затем саму папку. Используя обходной путь для ошибочного рекурсивного элемента удаления, решение будет выглядеть следующим образом:

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

Таким образом, вы также можете удалить родительский каталог.

Гоша. Множество ответов. Я честно предпочитаю этот всем им. Он очень простой, полный, читаемый и работает на любом компьютере с Windows. Он использует (надежную) функцию рекурсивного удаления .NET, и если по какой-то причине он не работает, он генерирует правильное исключение, которое можно обработать с помощью блока try / catch.

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

Обратите внимание, что Resolve-Path Строка важна, потому что .NET не знает ваш текущий каталог при разрешении относительных путей к файлам. Это единственное, что я могу придумать.

Вот что у меня работает:

$Target = "c:\folder_to_delete"

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

Remove-Item -Recurse -Force $Target

Эта первая строка удаляет все файлы в дереве. Второй удаляет все папки, включая верхнюю.

Сначала возьмите на себя ответственность за файлы / каталоги, используя Takeown.exe, затем удалите

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

У меня была проблема с каталогом, который нельзя было удалить. Я обнаружил, что одна из вложенных папок была повреждена, и когда я попытался переместить или переименовать этот дочерний каталог, я получил сообщение об ошибке, в котором говорилось о его отсутствии. Я попытался использовать rm -Force и получил ту же ошибку, что и вы.

У меня сработало сжатие родительского каталога с помощью 7-zip с установленной опцией «Удалить файлы после сжатия». После сжатия я смог удалить zip-файл.