PowerShell Remove-Item не ожидает - PullRequest
       44

PowerShell Remove-Item не ожидает

0 голосов
/ 08 ноября 2018

Если есть этот кусок кода

if(Test-Path -Path $OUT) 
{ 
    Remove-Item $OUT -Recurse 
}
New-Item -ItemType directory -Path $OUT

Иногда это работает, но иногда строка New-Item выдает ошибку PermissionDenied ItemExistsUnauthorizedAccessError . Что, как я полагаю, означает, что предыдущий Remove-Item еще не был полностью выполнен, и папка не может быть создана, поскольку она все еще существует.

Если я вставлю там сон

if(Test-Path -Path $OUT) 
{ 
    Remove-Item $OUT -Recurse 
    Start-Sleep -s 1
}
New-Item -ItemType directory -Path $OUT

тогда это работает всегда. Как я могу заставить Remove-Item убедиться, что папка действительно удалена? Или, может быть, я что-то пропустил?

Ответы [ 4 ]

0 голосов
/ 28 ноября 2018

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

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

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

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

полезный ответ Марша предлагает обходной путь : вместо воссоздав каталог, просто очистите его и используйте его повторно, что обходит проблема.

Однако опорожнение каталог с Get-ChildItem $OUT -Recurse | Remove-Item -Recurse также подвержен периодическим сбоям , хотя, вероятно, реже.

Эта проблема касается не только Remove-Item PowerShell, но и cmd.exe *1047*, а также [System.IO.Directory]::Delete():

в .NET

Начиная с 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() работают надежно , поскольку они не в состоянии учитывать асинхронное поведение функций удаления файлов / каталогов API Windows :

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

0 голосов
/ 08 ноября 2018

Команда Remove-Item имеет известную проблему .

Попробуйте вместо этого:

if (Test-Path $OUT) 
{ 
    # if exists: empty contents and reuse the directory itself
    Get-ChildItem $OUT -Recurse | Remove-Item -Recurse
}
else
{
    # else: create
    New-Item -ItemType Directory -Path $OUT
}

Примечание:

  • Команда Get-ChildItem находит только не скрытые файлы и подкаталоги, поэтому очистка целевого каталога может быть не завершена;чтобы добавить скрытые элементы, добавьте -Force.

  • Аналогичным образом добавьте -Force к -RemoveItem, чтобы принудительно удалить файлы, которые имеют только для чтения набор атрибутов.

    • Без -Force опорожнение может снова быть неполным, но в этом случае вы получите нескончаемые ошибки;если вы хотите рассматривать их как завершающие ошибки, добавьте также -ErrorAction Stop.
0 голосов
/ 08 ноября 2018

Для полноты картины: вы также можете использовать безопасные и быстрые методы .NET:

if ([System.IO.Directory]::Exists($OUT)) {
    [System.IO.Directory]::Delete($OUT, $true)
}
[System.IO.Directory]::CreateDirectory($OUT)

Примечание:

В зависимости от того, где вы получите значение $OUT, вы можете сначала преобразовать его в полный путь, чтобы убедиться, что методы .NET удаляют правильный каталог (см. Комментарий @ mklement0):

$fullPath = Convert-Path $OUT
0 голосов
/ 08 ноября 2018

Если вы введете Get-Help Remove-Item -Detailed, вы увидите:

Example 4: Delete files in subfolders recursively
PS C:\>Get-ChildItem * -Include *.csv -Recurse | Remove-Item

This command deletes all of the CSV files in the current folder and all subfolder recursively.

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

Делать то, что рекомендует спецификация:

if(Test-Path -Path $OUT) 
{ 
    Get-ChildItem $OUT -Recurse | Remove-Item
}
New-Item -ItemType directory -Path $OUT
...