Как обнаружить и пропустить заблокированные файлы в скрипте 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
        }
    }
}
Другие вопросы по тегам