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?
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:
- test a video for availability as above
- answer: available!
- in the meantime, a recorder starts up and locks the video
- back in your program, you try to move the video, but it fails with a sharing violation
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 ...
}
}
A windows API exists that will list the number of processes that keep a file open. It is the Restart Manager (Rststmgr.dll). Based on artical How do I find out which process has a file open?, and with help of Win32::API you can transform the program in a Perl program that tells you the number of processes that have a file open and even list the process ids.
Each test should be a sequence of RmStartSession, RmRegisterResources, RmGetList, RmEndSession. Use encode("UTF-16LE",$filename)
to make a CWSTR
type string.
精彩评论