简体   繁体   中英

Using a try block to test if a file is locked in powershell

I want to know if its bad form to use try blocks to test if a file is locked. Here's the background. I need to send text output of an application to two serial printers simultaneously. My solution was to use MportMon, and a Powershell script. The way it's supposed to work is the application default prints to the MportMon virtual printer port, which actually makes a uniquely named file in a "dropbox" folder. The powershell script uses a filesystemwatcher to monitor the folder and when a new file is created, it takes the textual content and pushes it out two serial printers, then deletes the file, so as not to fill up the folder. I was having a problem when trying to read the text from the file that the virtual printer created. I found that I was getting errors becasue the file was still locked. To fixed the problem, I used a FSM to impliment the logic and instead of checking for a lock everytime before attempting to get the content from the file, I used a try block that attempts to read content from the file, if it fails, the catch block just reaffirms the state that the FSM is in, and the process is repeated until successful. It seems to work fine, but I've read somewhere that its bad practice. Is there any danger in this method, or is it safe and reliable? Below is my code.

$fsw = New-Object system.io.filesystemwatcher
$q = New-Object system.collections.queue
$path = "c:\DropBox"
$fsw.path = $path
$state = "waitforQ"
[string]$tempPath = $null

    Register-ObjectEvent -InputObject $fsw -EventName created -Action {
    $q.enqueue( $event.sourceeventargs.fullpath )
    }

    while($true) {
    switch($state)
    {
        "waitforQ" {
                    echo "waitforQ"
                    if ($q.count -gt 0 ) {$state = "retrievefromQ"}
                    }
        "retrievefromQ"  {
                    echo "retrievefromQ"
                    $tempPath = $q.dequeue()
                    $state = "servicefile"
                    }
        "servicefile"  {
                        echo " in servicefile "

                    try
                        {
                        $text = Get-Content -ErrorAction stop $tempPath 
                        #echo "in try"
                        $text | out-printer db1
                        $text | out-printer db2
                         echo " $text "
                        $state = "waitforQ"
                        rm $tempPath
                        }
                    catch
                        {
                        #echo "in catch" 
                        $state = "servicefile"
                        }
                    }

           Default {    $state = "waitforQ"  }
    }
    }

I wouldn't say it's bad practice to test a file to see if it's locked, but it's not as clean as checking the handles used by other processes. Personally I'd test the file like you do, but I adjust a few parts to make it safer/better.

  • That switch-statement looks way to complicated (for me), I'd replace it with a simple if-test. "If files in queue, proceed, if not, wait".
  • You need to slow down.. You will try to read the file as many times as possible while it's locked. This is a waste of resources since it will take some time for the current application to let it go and save the data to a HDD. Add some pauses. You won't notice them, but your CPU will love them. The same applies when there are no files in the queue.
  • You might benefit from adding a timeout, like max 50 attempts to read the file, to avoid the script getting stuck if one specific file is never released.

Try:

$fsw = New-Object system.io.filesystemwatcher
$q = New-Object system.collections.queue
$path = "c:\DropBox"
$fsw.path = $path
$MaxTries = 50  #50times * 0,2s sleep = 10sec timeout
[string]$tempPath = $null

Register-ObjectEvent -InputObject $fsw -EventName created -Action {
    $q.enqueue( $event.sourceeventargs.fullpath )
}

while($true) {
    if($q.Count -gt 0) {

        #Get next file in queue
        $tempPath = $q.dequeue()

        #Read file
        $text = $null
        $i = 0
        while($text -eq $null) {

            #If locked, wait and try again
            try {
                $text = Get-Content -Path $tempPath -ErrorAction Stop
            } catch {
                $i++
                if($i -eq $MaxTries) {
                    #Max attempts reached. Stops script
                    Write-Error -Message "Script is stuck on locked file '$tempPath'" -ErrorAction Stop
                } else {                
                    #Wait
                    Start-Sleep -Milliseconds 200
                }

            }
        }

        #Print file
        $text | Out-Printer db1
        $text | Out-Printer db2
        echo " $text "

        #Remove temp-file
        Remove-Item $tempPath

    }

    #Relax..
    Start-Sleep -Milliseconds 500

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM