Batch sending a large number of emails by Perl

I occasionally help my friends who are running a company regularly dealing with hundreds of customers. Sending multiple emails based on a single sending list requires a kind of batch email construction and sending. In this article, I introduce a Perl script approach to accomplish that kind of tasks.

For an introduction to sending an email from the command line by Perl, see this previous article.

Here is an index to this article:

Overview

Let's take a look at the overview of our approach. Based on the sending list and the message body template, we dynamically generate emails with proper headers and contents. sendEmail program then send out the emails to the local MTA (Mail Transfer Agent). The emails are relayed through SMTP servers in the Internet, and finally reach recipients. We assume that our local MTA is already set up properly. In Ubuntu, the default MTA is Postfix (an SMTP server program). A Perl script send-email-bylist.pl handles everything prior to MTA stuffs.


Overview of sending a large number of emails

File resources

  • sendEmail: A Perl script software that handles all the email-spec-related stuffs to construct and send emails. If you are using Ubuntu, you may install it with
    $ sudo apt-get install sendemail
    

    If sendEmail is not included in the repository of your Linux distribution (or you are using MacOS or Windows), you may still download a copy from sendEmail's website.

  • list-companies.txt: The Email address list file. For each line, recipient name is followed by an email address. In general, a recipient name can contain white spaces. So we delimit recipient name and email address by ~~ instead of white spaces. We allow duplicate recipient names with different email addresses. The following example list contains only 4 destinations, however, the number of email addresses in the list can scale arbitrarily without limit!
    Company A~~<company-a@example.com>
    Company B~~<company-b1@example.com>
    Company B~~<company-b2@example.com>
    Company C~~<company-c@example.com>
    
  • body-template.txt: The message body template file used to generate per-recipient email messages. In this case, words with quotes !! are substituted recipient names in the email list file. The file looks as follows:
    Dear recipient at !!company!!,
    
    This is email message body for !!company!!.
    
    Sincerely,
    Taro Yamada
    
  • send-email-bylist.pl: The main Perl script developed in this article.

How to run the program

Running the program is straightforward. Follow the following steps. To adapt to your real-world situation, you should at least modify the From and Subject fields, the message body file, and the sending list file.

  1. From the previous File Resources section, get all required files (list-companies.txt, body-template.txt, and send-email-bylist.pl) and save them in the same directory.
  2. Make sure that the Perl scripts have executable permission:
    $ chmod 775 send-email-bylist.pl
    
  3. Run the main Perl script.
  4. $ ./send-email-bylist.pl
    

Details

Following is the main Perl script send-email-bylist.pl used in this article.

send-email-bylist.pl

#!/usr/bin/perl

use strict;

my $from = 'Taro Yamada <taro@example.net>';
my $subj = 'English Title';

open(LIST, "list-companies.txt") or die "Can't open list: $!\n";
while ( my $line = <LIST> ) {
    chomp($line);    # Remove a newline character from the end of a string
    (my $company, my $email) = split("~~", $line);
    my $to = $company . " " . $email;

    my $cmd = "cat ./body-template.txt | sed 's/!!company!!/$company/' > body-real.txt";
    print "$cmd\n";
    system("$cmd");

    $cmd = "sendEmail " .
           "-f \"$from\" " .
           "-t \"$to\" " .
           "-u \"$subj\" " .
           "-o message-file=body-real.txt " .
           "-s localhost:25" .
           "\n";
    print $cmd;
    system("$cmd");

    system("rm -f body-real.txt");
    system("sleep 2");
}
open(LIST, "list-companies.txt") or die "Can't open list: $!\n";
while ( my $line = <LIST> ) {
    chomp($line);    # Remove a newline character from the end of a string
    (my $company, my $email) = split("~~", $line);
    my $to = $company . " " . $email;

Open the sending list. For each line, extract a recipient name and a email address delimited by ~~, and form a To header.

    my $cmd = "cat ./body-template.txt | sed 's/!!company!!/$company/' > body-real.txt";
    print "$cmd\n";
    system("$cmd");

Carry out substitution of company names in the template body file. The output is saved as body-real.txt in the local directory. This file is used as the real message part by sendEmail program later.

    $cmd = "sendEmail " .
           "-f \"$from\" " .
           "-t \"$to\" " .
           "-u \"$subj\" " .
           "-o message-file=body-real.txt " .
           "-s localhost:25" .
           "\n";
    print $cmd;
    system("$cmd");

    system("rm -f body-real.txt");
    system("sleep 2");

The core part of constructing and send an email where sendEmail command is executed. After sending each email, we clean up its message file. To avoid congestion, we pose 2 seconds at the end of the loop.