Perl for System Administration
Build a practical sysadmin script that scans a directory, inspects file sizes and dates, and writes a backup-listing report.
Introduction
System administration is one of Perl's oldest and strongest use cases: quickly written scripts that inspect the filesystem, generate reports, and automate repetitive maintenance tasks. This lesson builds a small but realistic backup-listing script from the ground up.
- How to list the contents of a directory.
- How to read file size and modification time with stat.
- How to filter files by size or age.
- How to assemble those pieces into a working report-generating script.
Why Perl for Sysadmin Tasks
Perl ships by default on almost every Unix-like system, has direct, low-level access to filesystem and process functions, and lets you write a working script in a few lines instead of a few hundred. That combination made it the default automation language for system administrators for decades, and it is still common in that role today.
Scanning a Directory
opendir and readdir list the contents of a directory. readdir includes the special entries "." and ".." (the current and parent directory), which you almost always want to filter out.
use strict;use warnings;
my $dir = '.';opendir(my $dh, $dir) or die "Cannot open directory '$dir': $!\n";my @files = grep { !/^\.\.?$/ } readdir($dh);closedir $dh;
print "Found ", scalar(@files), " entries.\n";print "$_\n" for @files;Click Run to see what this code prints.
Getting File Metadata with stat
stat returns a fixed-length list of details about a file - permissions, owner, size, and three different timestamps. The two you will use most often are index 7 (size in bytes) and index 9 (last modification time, as a Unix epoch timestamp).
my @info = stat($filename);my $size = $info[7];my $mtime = $info[9];Filtering Files by Size
Combining directory scanning with stat lets you filter for files matching a condition - for example, only regular files (using the -f file test) above a certain size.
use strict;use warnings;
my $dir = '.';opendir(my $dh, $dir) or die "Cannot open directory '$dir': $!\n";my @files = grep { !/^\.\.?$/ && -f "$dir/$_" } readdir($dh);closedir $dh;
for my $file (@files) { my $path = "$dir/$file"; my @stat = stat($path); my $size = $stat[7]; printf "%-30s %8d bytes\n", $file, $size if $size > 1_000;}Building a Backup-Listing Script
Putting it all together: a script that scans a directory, gathers size and last-modified date for every file, and writes a clean report to a new file - the kind of small utility a sysadmin might run before or after a backup job.
use strict;use warnings;
my $dir = shift @ARGV // '.';my $report_file = 'backup_report.txt';
opendir(my $dh, $dir) or die "Cannot open directory '$dir': $!\n";my @files = grep { -f "$dir/$_" } readdir($dh);closedir $dh;
open(my $out, '>', $report_file) or die "Cannot open '$report_file': $!\n";printf $out "%-30s %12s %s\n", 'File', 'Size (bytes)', 'Modified';
for my $file (sort @files) { my $path = "$dir/$file"; my @stat = stat($path); my ($size, $mtime) = @stat[7, 9]; my @t = localtime($mtime); my $date = sprintf('%04d-%02d-%02d', $t[5] + 1900, $t[4] + 1, $t[3]); printf $out "%-30s %12d %s\n", $file, $size, $date;}
close $out;print "Backup report written to $report_file\n";Click Run to see what this code prints.
Common Mistakes
- Forgetting to filter out "." and ".." from readdir results.
- Forgetting the -f file test and accidentally trying to stat or process directories as if they were files.
- Using relative filenames from readdir without joining them back to the directory path.
- Hardcoding stat's numeric indices everywhere instead of naming them, which hurts readability in larger scripts.
- Assuming readdir returns entries in a useful order - it does not, so sort explicitly when order matters.
Best Practices
- Always join a directory and filename explicitly, e.g. "$dir/$file", rather than assuming the current directory.
- Check every open, opendir, and system call for failure and die with a clear message.
- Consider the core module File::stat for named field access (like $st->size) instead of numeric indices.
- Test scripts that touch the filesystem on a throwaway directory before pointing them at real data.
- Sort file lists explicitly whenever the report needs a predictable order.
Frequently Asked Questions
-M returns a file's age in days since modification, -A since last access, and -C since the inode's status last changed - all relative to when the script started running.
No - the order is filesystem-dependent. Always sort explicitly if the order of your report matters.
Yes, particularly on systems where Perl is already installed by default and for maintaining existing Perl-based tooling, though many teams now also reach for Python for new scripts.
Key Takeaways
- opendir/readdir/closedir list directory contents; always filter "." and "..".
- stat returns size at index 7 and modification time at index 9.
- The -f file test distinguishes regular files from directories and other entries.
- Combining directory scanning, stat, and printf builds practical filesystem reports quickly.
Summary
Directory scanning and file metadata are the backbone of countless sysadmin scripts, from backup verification to disk-usage reports. Next, you will apply similar techniques to a different but equally common task: analyzing log files with regular expressions.