Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine whether a file is in use in Perl on Windows

I'm writing some Perl which takes TV shows recorded on Windows Media Center and moves/renames/deletes them depending on certain criteria.

Since the Perl runs fairly frequently, I'd like to cleanly determine whether or not the file is in use (in other words, the show is in the process of being recorded) so I can avoid doing anything with it.

My current method looks at the status of a file (using "stat") and compares it again after 5 seconds, like so:

sub file_in_use
{
  my $file = shift;

  my @before = stat($file);
  sleep 5;
  my @after = stat($file);

  return 0 if ($before ~~ $after);
  return 1;
 }

It seems to work, but I'm concious that there is probably a better and cleaner way to do this.

Can you please advise?

like image 939
Richard Avatar asked Jul 10 '10 17:07

Richard


People also ask

How do I check if a file is empty in Perl?

-z: check if the file is empty. -s: check if the file has nonzero size (returns size in bytes).

How do you check if it is a file or directory in Perl?

The -f operator is used to identify regular files rather than directories or other types of files.

Which of the following will check whether file exists or not?

The '-e' option is used to check whether a file exists regardless of the type, while the '-f' option is used to return true value only if the file is a regular file (not a directory or a device). The most common option to check if the file exists or not is to use the test command with the 'if conditional statement'.


2 Answers

If the recording process locks the file, you could attempt to open it in read-write mode and see if it fails with ERROR_SHARING_VIOLATION as GetLastError (accessed via Perl's $^E special variable).

For example:

#! /usr/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 file ..\n" }

die usage unless @ARGV;

foreach my $path (@ARGV) {
  print "$path: ";

  if (open my $fh, "+<", $path) {
    print "available\n";
    close $fh;
  }
  else {
    print $^E == 0x20 ? "in use by another process\n" : "$!\n";
  }
}

Sample output with Dir100526Lt.pdf open by the Adobe reader:

C:\Users\Greg\Downloads>check-lock.pl Dir100526Lt.pdf setup.exe
Dir100526Lt.pdf: in use by another process
setup.exe: available

Be aware that any time you first test a condition and then later act based on the result of that test, you're creating a race condition. It seems that the worst this could bite you in your application is in the following unlucky sequence:

  1. test a video for availability as above
  2. answer: available!
  3. in the meantime, a recorder starts up and locks the video
  4. back in your program, you try to move the video, but it fails with a sharing violation
like image 147
Greg Bacon Avatar answered Oct 05 '22 07:10

Greg Bacon


The only improvement I would suggest is to stat all of your files at once, so you only need to sleep for 5 seconds one time instead of sleeping 5 seconds for every file:

my (%before, %after);
foreach my $file (@files_that_might_be_in_use) {
    $before{$file} = [ stat $file ];
}
sleep 5;
foreach my $file (@files_that_might_be_in_use) {
    $after{$file} = [ stat $file ];

    if ( $before{$file} ~~ $after{$file} ) {
        # file is not in use ... 
    } else {
        # file is in use ... 
    }
}
like image 25
mob Avatar answered Oct 05 '22 05:10

mob