Xanagandr Xanagandr - 7 days ago 4x
Perl Question

Interpreting & modifying Perl one-liner?

I have the following Perl 'one-liner' script (found it online, so not mine):

perl -lsne '
/$today.* \[([0-9.]+)\]:.+dovecot_(?:login|plain):([^\s]+).* for (.*)/
and $sender{$2}{r}+=scalar (split / /,$3)
and $sender{$2}{i}{$1}=1;

foreach $sender(keys %sender){
printf"Recip=%05d Hosts=%03d Auth=%s\n",
scalar (keys %{$sender{$sender}{i}}),
' -- -today=$(date +%F) /var/log/exim_mainlog | sort

I've been trying to understand its innards, because I would like to modify it to re-use its functionality.

Some questions I got:

  • What does the flag -lsne does? (From what I know, it's got to be, at least, 3 different flags in one)

  • Where does $sender gets its value from?

  • What about that (?:login|plain) segment, are they 'variables'? (I get that's ReGex, I'm just not familiarized with it)

What I'm trying to achieve:

  • Get the number of emails sent by each user in a SMTP relay periodically (cron job)

  • If there's an irregular number of emails (say, 500 in a 1-hour timespan), do something (like shutting of the service, or send a notification)

Why I'm trying to achieve this:
Lately, someone has been using my SMTP server to send spam, so I would like to monitor email activity so they stop abusing the SMTP relay resources. (Security-related suggestions are always welcomed, but out of topic for this question. Trying to focus on the script for now)

What I'm NOT trying to achieve:

  • To get the script done by third-parties. (Just try and point me in the right direction, maybe an example)

So, any suggestions, guidance,and friendly comments are welcomed. I understand this may be an out-of-topic question, yet I've been struggling with this for almost a week and my background with Perl is null.

Thanks in advance.

mob mob

-MO=Deparse is your friend for understanding one-liners (and one liners that wrap into five lines on your terminal):

$ perl -MO=Deparse -lsne '/$today.* \[([0-9.]+)\]:.+dovecot_( ...

BEGIN { $/ = "\n"; $\ = "\n"; }


while ( defined($_ = <ARGV>) ) {

    chomp $_;

    $sender{$2}{'i'}{$1} = 1 if
        /$today.* \[([0-9.]+)\]:.+dovecot_(?:login|plain):([^\s]+).* for (.*)/
            and $sender{$2}{'r'} += scalar split(/ /, $3, 0);

    sub END {

        foreach $sender (keys %sender) {

            printf "Recip=%05d Hosts=%03d Auth=%s\n", 
                scalar keys %{$sender{$sender}{'i'};}, $sender;

-e syntax OK

[newlines and indentation added for clarity]