Как обнаружить и пропустить заблокированные файлы в скрипте PowerShell?

7502
user1210394

Так что у меня есть сценарий PowerShell, который после многих головных болей я начал работать. Т.е. удаляет файлы, которые мне больше не нужны, и все отлично. Только одна проблема, он удалит файл независимо от того, открыт он другой программой или нет, что плохо. Мой код выглядит следующим образом:

# Change the value $oldTime in order to set a limit for files to be deleted. $oldTime = [int]30 # 30 days foreach ($path in Get-Content "pathList.txt") { # Write information of what it is about to do Write-Host "Trying to delete files older than $oldTime days, in the folder $path" -ForegroundColor Green # deleting the old files Get-ChildItem $path -Recurse -filter "*EDI*" | WHERE {$_.LastWriteTime -le $(Get-Date).AddDays(-$oldTime)} | Remove-Item -Force 

Мне просто нужен сценарий, чтобы увидеть, что файл открыт, пропустить указанный файл и двигаться дальше. Я использую PowerShell 2.0 на Windows 7 SP1.

1
Вы уверены, что файлы заблокированы при открытии? Часто программы создают временную копию (или просто полностью загружают ее в память), а затем снимают блокировку файла, чтобы другие могли получить к ней доступ. Когда вы сохраните следующее, он заблокирует файл достаточно долго, чтобы вернуть его обратно. Таким образом, в принципе, не может быть способа определить, используется ли файл или нет - в зависимости от программы, которая его открыла. Ƭᴇcʜιᴇ007 9 лет назад 0
Например, я тестировал папку, в которой были MP3. Когда я пытался удалить все в этой папке, он удалил все, кроме того файла, который я слушал, но игра меня ошибки. Это сказало, что это использовалось другим процессом. Просто нужен способ пропустить эти файлы на случай, если я столкнусь с этой проблемой. Но в основном он будет использоваться для удаления текстовых файлов. user1210394 9 лет назад 0

1 ответ на вопрос

1
beatcracker

Generally, trying to test is file locked or not can lead to all sorts of race conditions, because the file could become locked by another thread/process just after our check. And checking requires lock itself, unless it's not done via Restart Manager API which is available only from Windows Vista (see this answer). So you've been warned.

Here is the PowerShell function, that will check whether file is locked or not. Adapted to PowerShell from this question: https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use

Copy-paste it or save alongside with your script as Test-IsFileLocked.ps1 and use dot-sourcing to load:

$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path . (Join-Path -Path $ScriptDir -ChildPath 'Test-IsFileLocked.ps1') 

And then change the last line of your script to:

Get-ChildItem $path -Recurse -filter "*EDI*" | WHERE {($_.LastWriteTime -le $(Get-Date).AddDays(-$oldTime)) -and !(Test-IsFileLocked -Files $_.FullName)} | Remove-Item -Force 

Test-IsFileLocked function itself:

function Test-IsFileLocked { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [string[]]$Files ) Process { # Foreach loop to accept arrays either from pipeline or Files parameter foreach ($file in $Files) { $Locked = $false try { # Try to open file $Test = [System.IO.File]::Open($file, 'Open', 'ReadWrite', 'None') # Close file and dispose object if succeeded $Test.Close() $Test.Dispose() } catch { # File is locked! $Locked = $true } # Write file status to pipeline $Locked } } }