Functions/Public/Test-FileLock.ps1
|
function Test-FileLock { <# .SYNOPSIS Tests whether a file is currently locked by another process. .DESCRIPTION The Test-FileLock function checks if a file can be opened with read/write access. If the file is locked, it can either return a boolean, wait for user input, or wait with a timeout for the file to become available. .PARAMETER FilePath The path to the file to check. .PARAMETER Silent If specified, returns $true if the file is locked, $false if available. Does not display any messages or wait for input. .PARAMETER Wait If specified, waits for user to press Enter when file is locked. This is the default interactive behavior. .PARAMETER TimeoutSeconds If specified, waits up to this many seconds for the file to become available. Returns $true if file becomes available, $false if timeout expires. .PARAMETER RetryIntervalSeconds When using TimeoutSeconds, how often to retry. Default is 2 seconds. .INPUTS None. You cannot pipe objects to Test-FileLock. .OUTPUTS System.Boolean when using -Silent or -TimeoutSeconds. None when using interactive mode (default). .EXAMPLE Test-FileLock -FilePath "C:\Data\report.xlsx" Checks if the file is locked. If locked, prompts user to close it and press Enter. .EXAMPLE Test-FileLock -FilePath "C:\Data\report.xlsx" -Silent Returns $true if file is locked, $false if available. No user interaction. .EXAMPLE if (Test-FileLock -FilePath $file -Silent) { Write-Warning "File is in use, skipping..." } Use in scripts to check lock status without interaction. .EXAMPLE Test-FileLock -FilePath "C:\Data\report.xlsx" -TimeoutSeconds 30 Waits up to 30 seconds for the file to become available. .NOTES Author: Sune Alexandersen Narud Version: 1.0.0 Date: February 2026 .LINK Remove-StaleFile #> [CmdletBinding(DefaultParameterSetName = 'Interactive')] [OutputType([bool], ParameterSetName = 'Silent')] [OutputType([bool], ParameterSetName = 'Timeout')] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [string]$FilePath, [Parameter(ParameterSetName = 'Silent')] [switch]$Silent, [Parameter(ParameterSetName = 'Interactive')] [switch]$Wait, [Parameter(ParameterSetName = 'Timeout')] [ValidateRange(1, 3600)] [int]$TimeoutSeconds, [Parameter(ParameterSetName = 'Timeout')] [ValidateRange(1, 60)] [int]$RetryIntervalSeconds = 2 ) process { # Check if file exists if (-not (Test-Path $FilePath)) { if ($Silent -or $TimeoutSeconds) { return $false # File doesn't exist, so it's not locked } return # Nothing to do in interactive mode } # Function to check if file is locked $checkLock = { param($path) try { $stream = [System.IO.File]::Open($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) $stream.Close() return $false # Not locked } catch { return $true # Locked } } switch ($PSCmdlet.ParameterSetName) { 'Silent' { return (& $checkLock $FilePath) } 'Timeout' { $elapsed = 0 while ($elapsed -lt $TimeoutSeconds) { if (-not (& $checkLock $FilePath)) { return $true # File is available } Write-Verbose "File is locked, retrying in $RetryIntervalSeconds seconds... ($elapsed/$TimeoutSeconds)" Start-Sleep -Seconds $RetryIntervalSeconds $elapsed += $RetryIntervalSeconds } return $false # Timeout expired, file still locked } 'Interactive' { $fileName = [System.IO.Path]::GetFileName($FilePath) $folderName = [System.IO.Path]::GetDirectoryName($FilePath) while (& $checkLock $FilePath) { Write-Host Write-Host "The file '" -NoNewline Write-Host "$fileName" -ForegroundColor Cyan -NoNewline Write-Host "' in folder '" -NoNewline Write-Host "$folderName" -ForegroundColor Cyan -NoNewline Write-Host "' is currently locked or open in another program." Write-Host "Please close it and press Enter to continue..." Read-Host } } } } } |