Date Created: 2025-04-18
By: 16BitMiker
[ BACK.. ]
This guide walks you through how to run a Perl script on a regular schedule using systemd services and timers on a Debian-based system. We'll use a real-world example: a script that updates the modification time of HTML files based on embedded date strings.
Whether you're automating a blog, syncing timestamps, or just learning systemd, this guide will help you build a reliable and maintainable timer-based setup using best practices.
The script in question, touch.pl
, lives in:
xxxxxxxxxx
/var/www/scripts/touch.pl
It performs the following:
Recursively scans /var/www/miker.blog/
for .html
files
Extracts embedded dates from tags like <span>2025-04-19</span>
Uses Perl's Time::Piece
to convert the date to epoch time
Updates the file's mtime
and atime
using utime
Logs each file it successfully updates
This is ideal for workflows where you want ls -lt
to show logical, content-driven update times rather than filesystem timestamps.
Hereβs the script with detailed commentary:
xxxxxxxxxx
#!/usr/bin/env perl
use qw(say);
use qw($RealBin); # Finds the script's directory
use :: qw(:constants); # For colored terminal output
use :: ; # For parsing date strings into epoch
# Construct path to the blog directory
my $dir = $RealBin =~ s~scripts(?:/)?$~~ . q(miker.blog/);
# Use `find` to locate all .html files in the blog directory
map {
chomp;
my $file = $_;
# Open each file to read contents
open my $fh, q(<), $file or die qq(> Can't open file so fail time!\n);
my $html = join '', <$fh>;
close $fh;
# Look for a date inside a <span> tag, e.g. <span>2025-04-19</span>
if ($html =~ m~<(span)>(?::)? (\d{4}-\d{2}-\d{2})</(?1)>~) {
my $date = $2;
say q(> Changing time [), $file, q(] to [), $2, q(]);
# Convert date to epoch
my $epoch = ::-> ($date, '%Y-%m-%d')-> ;
# Update file access and modification times
utime($epoch, $epoch, $file) or die "> utime failed: $!";
}
} grep { m~html$~ } split m~\n~, qx(find '$dir');
Before systemd can run the script:
Ensure itβs executable:
xxxxxxxxxx
sudo chmod +x /var/www/scripts/touch.pl
Make sure all parent directories are executable (searchable) by systemd:
xxxxxxxxxx
sudo chmod o+rx /var /var/www /var/www/scripts
Consider ownership:
You can safely run it as root
, or
Run it as www-data
if permissions allow (discussed later)
A service defines how your script runs.
Create the service unit at /etc/systemd/system/touchpl.service
:
xxxxxxxxxx
sudo tee /etc/systemd/system/touchpl.service > /dev/null <<'EOF'
[Unit]
Description=Run touch.pl to update timestamps on HTML files
[Service]
Type=oneshot
# Define a safe environment for utilities like `env`, `perl`, `find`
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/var/www/scripts/touch.pl
EOF
Type=oneshot
: For short-lived jobs that don't stay running
Environment=...
: Ensures env perl
and find
resolve correctly without relying on login shells
ExecStart
: Runs your Perl script using the shebang (#!/usr/bin/env perl
)
This defines when the service will run.
Create the file /etc/systemd/system/touchpl.timer
:
xxxxxxxxxx
sudo tee /etc/systemd/system/touchpl.timer > /dev/null <<'EOF'
[Unit]
Description=Hourly timer for touch.pl
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
EOF
OnCalendar=hourly
: Runs every hour at the top of the hour
Persistent=true
: If the system was off during a scheduled time, it runs it after boot
You can change OnCalendar
to daily
, weekly
, or even a custom format like OnCalendar=Mon..Fri 08:00
.
Once both files are in place:
xxxxxxxxxx
sudo systemctl daemon-reexec # Reload systemd manager if recently upgraded
sudo systemctl daemon-reload # Reload unit files
sudo systemctl enable --now touchpl.timer
This enables the timer at boot and starts it immediately.
xxxxxxxxxx
systemctl list-timers --all | grep touchpl
This shows when it last ran, when it will next run, and its active state.
xxxxxxxxxx
sudo journalctl -u touchpl.service
Or see real-time output:
xxxxxxxxxx
sudo journalctl -u touchpl.service -f
You donβt have to wait for the next timer tick. Run it manually:
xxxxxxxxxx
sudo systemctl start touchpl.service
You can confirm success via file timestamps or by checking the logs.
If you donβt want this job running as root, and the scriptβs directory/files are readable and writable by the web user, add this to the service:
xxxxxxxxxx
User=www-data
Ensure necessary access:
xxxxxxxxxx
sudo chown -R www-data:www-data /var/www/miker.blog
If you want logs outside of journalctl
, modify the service:
xxxxxxxxxx
StandardOutput=append:/var/log/touchpl.log
StandardError=append:/var/log/touchpl.log
Create the log file and set permissions:
xxxxxxxxxx
sudo touch /var/log/touchpl.log
sudo chown root:adm /var/log/touchpl.log
Step | Description |
---|---|
β Script in place | /var/www/scripts/touch.pl |
β Made executable | chmod +x |
β Created systemd service | touchpl.service |
β Created systemd timer | touchpl.timer |
β Enabled the timer | systemctl enable --now |
β Verified behavior | via list-timers , journalctl |
Your Perl timestamp script is now fully integrated into systemd and running automatically each hour. π