Search code examples
powershelldelete-filefile-in-use

Remove-Item (and [System.IO.File]::Delete() ) removes file that is in use


I am working on a script to do some cleanup of the many GB of cruft an Autodesk install leaves behind, and I am getting an error about some log file buried deep in the folder structure that is still in use. So, I want to get $_.exception.GetType().fullname so I can have a do/while loop that loops as long as that is the failure. Or more likely loops until success or a specified number of tries fails. To that end I created an RTF file on my C drive, opened it in Wordpad and tried this code to get the exception info.

$path = 'C:\New folder\New Rich Text Document.rtf'
try {
    Remove-Item $path -errorAction Stop
} catch {
    Write-Host "$($_.exception.GetType().fullname)"
    Write-Host "$($_.exception.message)"
}

Net result, without the -Force flag, the file was deleted even while open. Which I THINK really should not happen. Is this just something stupid like Microsoft never bothered to implement file locking with WordPad? Or is there some good reason for this behavior I don't understand? And more importantly, IS there a way to trigger this condition, so that I can actually write and test some code that responds gracefully? Ideally not using MS Office or the like, since I don't have it. Something included with the OS would be ideal. I have found this approach to doing it with PowerShell, so I could have a second script running that locks the file and loops for a time before releasing it, and as long as that time is less than my timeout on the delete it would work. But it seems like I should be able to, just open the file. Though it now occurs to me that the file causing the problem is a log file, so it is likely locked through a mechanism more like that link, rather than actually being "open". But it still seems to me that an open file should be delete-able.

Also, as noted in the title, I tried [System.IO.File]::Delete($path) as well, and to my surprise that deletes also. Curiouser and curiouser.


Solution

  • Some applications do not put a lock on a file when opened like for a text file MS Word does. Wordpad however does not, so it is possible to delete the file while it is opened in Wordpad..

    To test a file lock, I use a small helper funtion:

    function Test-LockedFile {
        param (
            [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [Alias('FullName', 'FilePath')]
            [ValidateScript({Test-Path $_ -PathType Leaf})]
            [string]$Path
        )
        $file = [System.IO.FileInfo]::new($Path)
        # old PowerShell versions use:
        # $file = New-Object System.IO.FileInfo $Path
    
        try {
            $stream = $file.Open([System.IO.FileMode]::OpenOrCreate,
                                 [System.IO.FileAccess]::Write,
                                 [System.IO.FileShare]::Delete)  # in this case for deletion
            if ($stream) { $stream.Close() }
            return $false
        }
        catch {
            return $true
        }
    }
    

    Now if I open a file in MS Word, and test that file

    Test-LockedFile 'D:\Test\blah.rtf'
    

    returns True

    When I open the same file in Wordpad, the test results in False.

    Another way to determine if a file is locked, although sneaky is to try and change the extension (or any file attribute for that matter) of that file. If opened in a locking application, you can't do that, but when opened in Wordpad, no problem..