Как обнаружить и пропустить заблокированные файлы в скрипте PowerShell?
Итак, у меня есть сценарий 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 ответ
Как правило, попытка проверить, заблокирован файл или нет, может привести к всевозможным состояниям гонки, поскольку файл может быть заблокирован другим потоком / процессом сразу после нашей проверки. А проверка требует самой блокировки, если только она не выполняется через API Restart Manager, который доступен только из Windows Vista (см. Этот ответ). Так что вы были предупреждены.
Вот функция PowerShell, которая проверяет, заблокирован ли файл или нет. Адаптирован к PowerShell из этого вопроса: https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use
Скопируйте и вставьте его или сохраните вместе с вашим скриптом как Test-IsFileLocked.ps1
и использовать точечный источник для загрузки:
$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
. (Join-Path -Path $ScriptDir -ChildPath 'Test-IsFileLocked.ps1')
А затем измените последнюю строку вашего скрипта на:
Get-ChildItem $path -Recurse -filter "*EDI*" | WHERE {($_.LastWriteTime -le $(Get-Date).AddDays(-$oldTime)) -and !(Test-IsFileLocked -Files $_.FullName)} | Remove-Item -Force
Test-IsFileLocked
сама функция:
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
}
}
}