How to monitor file changes in a directory using Perl?
Question
How to monitor file changes in a directory using Perl?
Monitoring file changes in a directory in Perl is typically done by periodically polling the directory contents and comparing file modification times (mtime), given that Perl's core does not provide event-driven file system notifications. This technique works across platforms without external modules, making it ideal for sandboxed or restricted environments.
Perl Concepts Used
opendir,readdir, andclosedirfor directory traversal.statto get file metadata, with the modification time at index 9.- Hashes (%hash) to store snapshots of file states for comparison.
- Context sensitivity:
statused in list context to access mtime. - Core module
Time::HiResto sleep for fractional seconds (available since Perl 5.8). - Use of
$|=1to autoflush STDOUT for immediate output display.
Important Fixes for Sandboxed Execution
- Use a fixed, small number of polling iterations (3) to prevent infinite loops and long run times.
- Reduce the polling interval so total runtime stays under 1 second.
- Print informative output immediately via autoflush to track changes clearly.
Runnable Perl Example: Poll Directory Changes 3 Times with 0.2s Interval
use strict;
use warnings;
use Time::HiRes qw(sleep);
$| = 1; # Autoflush STDOUT
my $dir = '.';
my %files_snapshot;
sub scan_directory {
my ($directory) = @_;
my %current;
opendir(my $dh, $directory) or die "Cannot open directory '$directory': $!";
while (my $file = readdir($dh)) {
next if $file eq '.' or $file eq '..';
my $path = "$directory/$file";
next unless -f $path; # Only regular files
my $mtime = (stat($path))[9];
$current{$file} = $mtime;
}
closedir($dh);
return %current;
}
# Initial snapshot
%files_snapshot = scan_directory($dir);
print "Starting directory monitoring on '$dir'\n";
for my $iteration (1 .. 3) {
sleep(0.2);
my %current = scan_directory($dir);
for my $file (keys %current) {
if (!exists $files_snapshot{$file}) {
print "Added file: $file\n";
}
elsif ($current{$file} != $files_snapshot{$file}) {
print "Modified file: $file\n";
}
}
for my $file (keys %files_snapshot) {
if (!exists $current{$file}) {
print "Deleted file: $file\n";
}
}
%files_snapshot = %current;
}
print "Monitoring completed.\n";
Explanation
This script:
- Takes an initial snapshot of all regular files in the current directory, storing their modification times.
- Runs 3 polling cycles, each pausing 0.2 seconds (keeping total run time under ~1 second).
- After each poll, compares the current file list to the snapshot, printing any added, modified, or deleted files.
- Updates the snapshot for the next iteration.
This avoids infinite loops and heavy filesystem load, making it safe for restricted sandboxes while demonstrating how to monitor changes with core Perl functions.
Common Pitfalls
- Infinite loops: Avoid
while(1)loops in time-constrained environments. - Timestamp granularity: Some file systems only update
mtimeto the nearest second, so rapid changes may be missed. - Symbolic links and directories: This example only considers regular files (
-f), so changes in directories or symlinks are not tracked. - Output buffering: Without
$|=1, output may be delayed or buffered, obscuring real-time changes. - Polling interval: Smaller intervals detect changes faster but consume more CPU.
This simple polling method is straightforward, portable, and suitable for sandboxed Perl environments where event-based watching modules like Linux::Inotify2 or File::ChangeNotify are unavailable.
Verified Code
Executed in a sandbox to capture real output. • v5.34.1 • 629ms
Starting directory monitoring on '.'
Monitoring completed.
(empty)