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?
-z: check if the file is empty. -s: check if the file has nonzero size (returns size in bytes).
The -f operator is used to identify regular files rather than directories or other types of files.
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'.
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:
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 ...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With