How do I detect and skip locked files in a PowerShell script?

9,678

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
        }
    }
}
Share:
9,678

Related videos on Youtube

user1210394
Author by

user1210394

Updated on September 18, 2022

Comments

  • user1210394
    user1210394 over 1 year

    So I have myself a PowerShell script that after much headaches I got working. Ii deletes files I no longer want and everything is great. Only one issue, it will delete a file whether it is open by another program or not, which is bad. My code is as follows:

    # 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
    

    I just need a way for the script to see that a file is open, skip said file and move on. I'm running PowerShell 2.0 on Windows 7 SP1.

    • Ƭᴇcʜιᴇ007
      Ƭᴇcʜιᴇ007 about 9 years
      Are you sure the files are locked when open? Often programs will make a temporary copy (or just load it fully into memory) and then release their file lock so that others can access it. When you save next it will lock the file just long enough to dump it back. So basically, there may be no way to determine if the file is in use or not - depending on the program that opened it.
    • user1210394
      user1210394 about 9 years
      For example I was testing on a folder that had MP3s in it. When I tried to delete everything in that folder it deleted everything but that file I was listening to, but game me errors. It said it was being used by another process. Just need a way to skip those files just in case I run into that issue. But it will mostly be used to delete txt files.