Twenty years ago, people used the number of phone calls they made in a day as a metric of how connected they were. Nowadays, people use the amount of email they receive each day. The truth about email, however, is that not all of it deserves equal time. Much of it might be email you don't want to see at all, but have agreed to receive in order to register on some company's Web site. Or maybe you receive it because, unbeknownst to you, someone sold your name and address. And a good bit of your email probably comes from legitimate discussion or workgroup-mailing lists that don't demand immediate attention.

The amount of attention you give to any email message is probably inversely proportional to the number of recipients. For example, you'd pay the least attention to messages from brainless spammers and nebulous corporate administrativia (most of which you'd probably delete), and devote most of your attention to email sent to you alone, with various discussion and workgroups lists falling somewhere in between.

Procmail brings order to the chaos of your inbox, and it does much more. Procmail, and the cluster of utilities associated with it, can take one or more actions on individual email messages based on the content of the headers or the body, or the size of the message. Once a specified condition is met, Procmail can file the email in a local mailbox (which could be /dev/null, effectively deleting it), forward it to another address, or pipe it as input to another command. Procmail can help manage your email in infinite ways.

What is Procmail?

Procmail is a Unix utility, written in C, that runs noninteractively as a filter and delivery agent for incoming email. Like many open source projects, Procmail has many parents, but the primary Procmail progenitors are Stephen R. van den Berg and Philip A. Guenther.

Some versions of Linux use Procmail as their default email delivery agent, using it to deliver email to local mailboxes. In this article, however, I'll focus on how to get started with Procmail and use it without it being installed as the delivery agent on your email server. In a follow-up article, I'll write about centralized Procmail installations and some of the scenarios that might appeal to those of a more gearheaded bent.

In case you're wondering, a Windows NT/Win2K version of Procmail is not likely anytime soon. (For more on why this is, go to the Procmail FAQ.) Several of the Unix core operating system features upon which Procmail depends are absent in Microsoft Windows, such as a setuid access-based model, or a native, local mail-delivery agent. Essentially, porting Procmail to Windows is like translating some poetry out of its native tongue--it ends up being ineffective gibberish.

As I alluded earlier, there are a few different ways to run Procmail. The most common way is to dovetail it with your mail transport agent and use it as your system-wide mail delivery agent, allowing it to perform all local mailbox delivery. Another way is to use Procmail or its constituent parts in special-purpose scripts to perform post-processing on your email. The method which I'm focusing on in this article is a "per user use." This method is for users who can access a mechanism, such as a .forward file, or some other method for instructing their mail server to forward/filter all their email through a specified command upon delivery.

In many cases, you'll have Procmail available to you already, but in case you don't, here's a brief description of the installation process.

Procmail Installation

You can get Procmail from links to canonical archives and many mirror sites on the Procmail Home Page. One URL is Once you've pulled down the source and expanded it into its installation tree, you're almost ready to compile and install it. There are a few things you may want to change first:

  1. Edit Makefile. If you're not doing a system-wide installation, you might want to edit Makefile. (It is in the top-level directory you created when you expanded procmail.tar.gz.) Change the value of BASENAME from /usr to a directory which you have administrative access over, like your home directory. Likewise, there are a handful of other variables, also near the top of the file, like BINDIR and MANDIR that let you tweak where Procmail will be installed once it's compiled. The default values for these additional variables are built upon the value of BASENAME, so you may want to only change BASENAME, if anything at all.

  2. Customize config.h. The second file you might want to customize is config.h. Each time Procmail starts up (typically, it's fired off once for every message it processes), it drops all environment variables from the parent process except the time-zone variable, TZ. If you've got critical environment variables you want to preserve for use in processing your email, you can add them to KEEPENV, alongside TZ.

  3. Customize the PATH variables. There are also two default PATH variables you may want to customize. To explain the difference between them, I need to indulge in a forward reference. Procmail can make use of both system-wide and user-specific configuration files. These configuration files consist of any number of "recipes." Procmail recipes are lists of conditions and instructions on how Procmail should handle each email message that meets those conditions. One of the aforementioned PATH variables, DEFSPATH, is the default system path that should be in effect when Procmail is processing system-wide, mail-handling recipes. The other, DEFPATH, is the default path that should be in effect when end-user recipes are processed.

Once you've changed your Makefile and config.h to your liking, you're ready to build Procmail and it's various components. In practice, I've always been able to do system-wide Procmail installs, and have never needed to change either file.

For more of the minutiae about the install process, read the INSTALL file in the top-level directory of the install tree. To kick off the install, issue the "make install" command. You'll soon be presented with a message like this:

In order for the kernel-locking tests to work as intended 
I have to be able to test-lock files on as many semantically 
different filesystems as possible (for more information 
about this, READ PARAGRAPH TWO in INSTALL). To suppress 
this prompt you can set the LOCKINGTEST variable in 
the Makefile.

Please add writable directories to the list. You should 
only add directories that reside on filesystems that have 
unique characteristics. E.g. if you have several remote 
NFS partitions, pick some unique client-server pairs, 
there is little use in picking the same server twice 
from the same client-machine. An excellent candidate 
would be a remotely mounted mail spool directory.

I will temporarily use a testdirectory named _locktest
in the following directories:

/tmp .

If you would like to add any, please specify them below,
press return to continue:

If you've got mailboxes mounted on other types of file systems than those used by the current directory and /tmp, enter them after the prompt, so the Procmail install process can test the locking semantics on each file system. This is especially important if some mailboxes are on shared local storage, or remotely mounted storage. As the previous caveat message in the Makefile admonishes, this is particularly important with NFS-mounted file systems. Again, in most cases, you can just press RETURN to continue, and marvel at how neat, clean and non-toxic Procmail builds.

Once the "make install" process is completed, make will display some advice about a further recommended step. If you're doing an install for system-wide install as superuser:

If you are a system administrator you should consider 
integrating procmail into the mail-delivery system -- 
for advanced functionality, speed AND SECURITY --. 
For more information about this topic you should look 
in the examples/advanced file.

Also, HIGHLY RECOMMENDED (type 'make install-suid' to execute it):

chown root /usr/bin/procmail
chgrp mail /usr/bin/procmail /usr/bin/lockfile
chmod 6755 /usr/bin/procmail
chmod 2755 /usr/bin/lockfile

If you're a system admininistrator doing a system-wide install, it would be prudent to issue the command "make install-suid" now to set the recommended permissions on the binaries you just made.

Now that your install is complete, here's a list of what you've got:

  • procmail
  • . This is the core of the Procmail package. Procmail takes RFC822-formatted messages on standard input and processes them according to both system-wide and user-specific recipe files.

  • lockfile
  • . A file locking utility that has built-in back-off mechanisms for scripts and rules that require more complex file-locking accommodations.

  • formail
  • . An easily addictive utility for munging your email. The two main uses for formail are in scripts and in Procmail recipes. Formail can be used to neatly split mailing list digests, to RFC822-icate wrongly formatted mail messages, or simply to construct a reply header for any arbitrary email messages. In fact, Dianna and I used formail in our book, Managing IMAP, as part of a system to help convert a mailstore from the Unix-mailbox format to the enhanced mx format native to the Cyrus IMAP server.

  • mailstat
  • . Mailstat generates a report based on the contents of your Procmail logfile. The report may not be totally accurate, since Procmail (by default) doesn't log each multiple action taken on a single message; only the final action is logged.

  • man pages
  • . A wealth of man pages for everything except the mailstat command. For that, you'll need to do a "mailstat -h" to get a usage message. Procmail, lockfile, and formail each have man pages. The format of the Procmail recipe file is in man page procmailrc (5). Numerous examples are explained in procmailex (5). The procmailsc (5) man page describes the Procmail weighted-scoring technique, where recipe conditions can be given a score and a comparative weight so you can give differing levels of importance to the rules in a given recipe. I'll talk more about this in my next article.

So much for how to get and install Procmail. Putting Procmail to use is as complex as you care to make it. Unconfigured, it works just fine doing nothing but sending email to your default mailbox; add a couple of recipes and you'll be well on your way to turning the frenzied battleground that is your inbox into a well-ordered and manageable handful of correspondence.

It's prudent to test Procmail before you put it into production. A good first step would be to check and confirm what defaults you compiled into Procmail. To learn more about testing, click here.

Now that you've verified that Procmail works, one step remains before you can start building your personal recipe file. If you're not the system administrator on your email server and your mail server doesn't have Procmail installed as the default delivery agent for your mail transport agent, you'll need to pipe your email through Procmail using a user-specific mechanism. For the purposes of this article, we'll assume that you use Sendmail as a mail transport agent, and that Sendmail is configured to check the $HOME/.forward file for a forwarding address. If you use something like Post.Office, Postfix, or QMail, your system administrator should be able to help you with a suitable variation for your site.

Usually, if you have a $HOME/.foward file, it will have one or more addresses to which your email should be forwarded, and the contents might look something like this:


Your .forward file, if you have one, probably only has one entry in it. Regardless, one of the approaches recommended by the procmail mini-FAQ is to make something like the following command the sole contents of your $HOME/.forward file:

"|IFS=' '&&p=/usr/bin/procmail&&test -f $p&&exec $p -f-||exit

This command is enclosed in quotes to ensure that Sendmail treats it as a single command-line. It's one of those cover-all-bases commands that even attempts to return an exit code of 75 on failure so the email message that was being processed would be requeued by Sendmail. The one change you should make to this command is to substitute your own userid (or some other guaranteed unique string) for YOUR-userid-HERE. Some versions of Sendmail attempt to reduce their workload by delivering to duplicate .forward files only once. This kludge assures that each Procmail invocation is unique enough to prevent this from happening.

Having said that, I've had perfectly good success by putting "|/usr/bin/procmail" in my $HOME/.forward. As long as they're valid, config file keystrokes are cheap, so I recommend you use the longer and somewhat more paranoid version.

Cooking up Recipes

Now, we're ready to start building a personal recipe file. Once you get started with Procmail, you'll find that your .procmailrc file is a living document--you can adjust it continuously to fit the changing profile of your incoming email. Over time you'll move from deleting spam and filing mailing lists to filing lists in folders for each day of the week, and to putting robotics into your recipe file to auto-respond to certain types of messages. Not only will Procmail help you bring order to chaos in your email, but it will empower you and your email to do more and more with a greater degree of control.

Each Procmail recipe file is a flat text file containing zero or more recipes, environment variables, and comments. As we've seen, there are some environment variables like VERBOSE that have an impact on what Procmail does. As per the procmailrc(5) man page, each recipe has a format like this:

:0 [flags] [ : [locallockfile] ]
<zero or more conditions (one per line)>
<exactly one action line>

For example, a Procmail recipe to forward all email to userid root on to a pager's email address might be:

# This address handles root mail just like an alias would.

We'll come back to flags later, ignore local lockfiles for now, and concentrate on the bare minimum required to get up and running with Procmail.

Each condition, consisting of an asterisk (*) followed by either a regular expression or another condition specifier, must be on a line by itself. Regular expressions are text patterns in a format spelled out in the procmailrc(5) man page. If you're already familiar with regular expression formats used in perl or egrep, you know most everything you need to know about Procmail regular expressions. Other conditions you can use are:

  • !regularExpression: If you wish to invert the expression.

  • $bourneAgain: To evaluate the "bourneAgain" string as if it were a Bourne shell expression.

  • ?someCommand: Uses the exit code of the specified command.

  • <someNumber: Succeeds if the size of the message is smaller than someNumber bytes.

  • >someNumber: Succeeds if the size of the message is greater than someNumber bytes.

  • someVariable condition: Matches someVariable against condition.

Each recipe must end with exactly one action line. The action line consists of one of the following:

  • ! An address preceded by a bang ("!") tells Procmail to forward the message to that address.

  • |some_command: The pipe symbol followed by a command instructs Procmail to pipe the message through that command.

  • /some/path/to/a/filename
    : In both of the examples above, Procmail will add the message to those mail folders. In the first case, the complete path of the folder is specified. In the second case, the location of the mail folder is assumed to be at $MAILDIR (a Procmail environment variable).

  • /some/path/to/a/directory
    : In each of the above cases, a directory is specified. In the first example, where the directory name doesn't end in a slash, Procmail puts the message in a guaranteed unique name in that directory. In the second example, where the directory name ends in a slash ("/"), Procmail will assume the directory is a Maildir format folder. In the third example, where the directory name ends in slash-dot ("/.") the directory is assumed to be an MH folder.

  • {: Action lines can begin with a left brace ("{"), which is a mechanism for nesting recipes. We won't be getting into that until my next article, however.

For example, if you want to have a Procmail recipe that saves all email from the Cheese Of the Day club in an appropriately named folder as soon as it arrives, you could have a recipe like this:

*^From:\ .*

That recipe would take all incoming email with a header (conditions act on headers unless otherwise instructed), starting with "From:", followed by a single space ("\ "), any number of other characters (".*"), and the address "" and file it into a mail folder named "cud". No reason to stop there, though. Let's say the cud mailing list is so active that you want to split it up into separate daily folders. One way to do that is to make an outside system call using a shell command within backticks to generate the output you want; in this case, the three-letter abbreviation for the current day of the week:

*^From:\ .*
cud.`date +"%a"`

In this example, you'd eventually have seven cud folders: cud.Sun, cud.Mon, cud.Tue, and so on. Let's say, though, that in addition to saving the message, you also want to be paged if there's a posting to the mailing list with the phrase "bow vine" in the subject. One way would be to use two Procmail recipes, the first of which would be flagged to continue. If it wasn't flagged, Procmail would cease processing on the first match. Here's an example of two rules, each of which will catch and process email from The second will also catch email that has the phrase "bow vine" in the subject line:

#even messages that match this recipe 
#will continue on after processing
#this recipe files all cud list mail in 'cud.folder'
*^From:\ .*
# This next recipe catches the same mail, checks to see 
# if it has 'bow vine' with any variant in capitalization
# in the subject line, the pipes it into the "sendapage" command.
*^From:\ .*
*^Subject:\ .*[Bb][Oo][Ww]\ [Vv][Ii][Nn][Ee]
|sendapage kwm

These are simple examples to illustrate one use of flags in a recipe. More than one flag can be used in a recipe. In addition to telling Procmail to continue after processing a recipe, there are also flags that tell Procmail to evaluate the message body instead of the headers; to be case-sensitive; and to continue only if the previous recipe was successful (or failed).

It's easy to see how easily you can hammer out some fairly complex recipes, and that a given recipe's performance depends not only on its own construction, but on where it sits in the overall recipe file and the construction of the recipes before it. For these reasons, it's a good idea (a WAY good idea) to spend some time testing your recipes once you begin adding to your recipe file.

Testing Recipes

Now that you've got a good feel for how to construct Procmail recipes, let's touch on how you can test them before putting them into production. There are a couple of schools of thought on this. One is that you can just slap a new recipe into your production file, test it in place, tweak it if necessary, then run with it. The more conservative route is to test it in a separate recipe file by manually piping a message to it and putting it in your production recipe file only after adequate testing. It would probably be more prudent to do the latter until you have sufficient confidence with Procmail, then switch to the former.

Let's test a couple of recipes using the latter method. First, we'll need to construct a recipe file. We'll have one forwarding recipe and one to save to a folder. Then we'll run three messages through it, one for each recipe and one that doesn't match at all to confirm messages will get to the default mailbox. Here's the contents of test-procmailrc, our sample recipe file:

# The default location for mail folders

# It's ALWAYS a good idea to have a logfile
# with Procmail.

# make sure DEFAULT has a value when invoking
# Procmail with a "-m" parameter and testing rcfiles.

*^From:\ .*

*^To:\ .*

Notice that we've loaded up default values to three important Procmail variables at the top. MAILDIR is the default location of any folders for which you haven't specified a path. It's always a good idea to let MAILDIR decide where your various folders live rather than prepending a path to the folder name. That way, you can eaisly move all folders to a new location by just changing the value of MAILDIR. LOGFILE is the file to which any logging output is written. I recommend you always use a log file. Sometimes, if an errant Procmail recipe causes your messages to go loopy or spinning off into nothingness, the audit trail left behind in your log file may hold the only clues to help in rectifying the situation. The variable DEFAULT holds the location of your main mailbox. Under ordinary circumstances, it would be unnecessary to define this. If you use the system default location (for the historic Unix-style mailbox store, that location would be /var/spool/mail/$LOGNAME).

Since we're specifying a Procmail recipe file on the command-line with the "-m" switch, we must set explicit values to both the MAILDIR and DEFAULT variables if we wish to recreate the common behavior for Procmail. If we didn't specify a value for MAILDIR, it would expect or place unqualified mail folders in the current directory, ".". If we didn't specify a value for DEFAULT, that variable would remain unset and no test messages would "fall through" to the default mailbox if they didn't match any recipes.

To test this recipe, save three email messages to a file and hand-edit them as necessary to they meet the recipes' conditions. Give one a "From: " address of "", another a "To: " address of "", and ensure that the third has neither. You can test them by piping them into Procmail with a command like this:

% cat ./sample.messages | \  
   /usr/bin/formail -q- -s procmail -tm VERBOSE=on \

I've broken this command up on three lines to show it here, but you would most likely type it on one. It begins by cat-ing the sample messages we just constructed into a formail command. We'll go deeper into the vast utility of formail in my next article. Suffice to say that we're using it as an email equivalent of the xargs command. As we're using it here, we've told formail (with the "-s" option) to split the input stream into separate RFC822 messages, issue the command "procmail -tm VERBOSE=on ./test-procmailrc" for each one, and pass it to the message on standard input. The "-q-" option tells formail to establish or refresh (if it already exists) the MTA "From " header line. Each procmail command is told (with the "-t" switch) to "fail softly" and not bounce email back to the sender (a Good Thing, when testing). Finally, the "-m" switch tells Procmail to use a user-specified recipe file, specifically ./test-procmailrc. Remember, we're going through these formail gyrations because we're simulating conventional delivery of messages through Procmail. In the real world, your mail transport agent (Sendmail, QMail, etc.) will both add the MTA "From " line to the beginning of the message, and pipe each of your messages to a separate Procmail process. A more common use of formail would be from complex recipes, or in separate scripts.

In the following example we'll set the value of VERBOSE on the command line to "on." With VERBOSE set to "on," we get output like this:

% cat ./sample.messages | \
    /usr/bin/formail -q- -s procmail -tm VERBOSE=on \
procmail: [13083] Mon Nov 20 00:09:35 2000
procmail: Assigning "MAILDIR=."
procmail: Rcfile: "./test-procmailrc"
procmail: Assigning "MAILDIR=/home/kwm/mail"
procmail: Assigning "LOGFILE=/home/kwm/procmail.logfile"
procmail: Opening "/home/kwm/procmail.logfile"
procmail: [13084] Mon Nov 20 00:09:35 2000
procmail: Assigning "MAILDIR=."
procmail: Rcfile: "./test-procmailrc"
procmail: Assigning "MAILDIR=/home/kwm/mail"
procmail: Assigning "LOGFILE=/home/kwm/procmail.logfile"
procmail: Opening "/home/kwm/procmail.logfile"
procmail: [13086] Mon Nov 20 00:09:35 2000
procmail: Assigning "MAILDIR=."
procmail: Rcfile: "./test-procmailrc"
procmail: Assigning "MAILDIR=/home/kwm/mail"
procmail: Assigning "LOGFILE=/home/kwm/procmail.logfile"
procmail: Opening "/home/kwm/procmail.logfile"

Notice that the Procmail VERBOSE output is actually from three separate procmail processes: 13083, 13084 and 13086. This output isn't terribly helpful except to confirm that you have indeed run Procmail, and that your variables were set to the anticipated values. The interesting stuff is found in your log file. In this case, the logfile's in /home/kwm/procmail.logfile:

procmail: Assigning "DEFAULT=/var/spool/mail/kwm"
procmail: No match on "^From:\ .*"
procmail: Match on "^To:\ .*"
procmail: Assigning "LASTFOLDER=secondrecipe.file"
procmail: Opening "secondrecipe.file"
procmail: Acquiring kernel-lock
From Fri Nov 17 08:49:31 2000
 Subject: blah
  Folder: secondrecipe.file                            1079
procmail: Assigning "DEFAULT=/var/spool/mail/kwm"
procmail: Match on "^From:\ .*"
procmail: Assigning "LASTFOLDER=/usr/sbin/sendmail -oi"
From Fri Nov 17 08:49:52 2000
 Subject: blah
  Folder: /usr/sbin/sendmail -oi    1101
procmail: Executing "/usr/sbin/sendmail,-oi,"
procmail: Assigning "DEFAULT=/var/spool/mail/kwm"
procmail: No match on "^From:\ .*"
procmail: No match on "^To:\ .*"
procmail: Locking "/var/spool/mail/kwm.lock"
procmail: Assigning "LASTFOLDER=/var/spool/mail/kwm"
procmail: Opening "/var/spool/mail/kwm"
procmail: Acquiring kernel-lock
procmail: Unlocking "/var/spool/mail/kwm.lock"
From Fri Nov 17 08:49:31 2000
 Subject: Some random message not directly hit by a recipe
  Folder: /var/spool/mail/kwm                          1166

Now this output is pleasingly atomic, or at the very least helpfully detailed. Granted, you don't want this level of detail all the time in your log file, but it's darn helpful when you're treading new ground. For each message, you see variables being set, and the results of each message being compared to each recipe condition, until there's a match and it reaches "delivered" condition. In an actual recipe file with many recipes, the output from VERBOSE can border on tedium, so it's really best to only use verbose output when testing small cases, or troubleshooting. Procmail will attempt to process each message against each recipe until it has been "delivered". In the Procmail world, "delivered" means that a message has been written to a file, piped into a program, or forwarded to another address. Procmail can be told to treat messages that meet this criteria as non-delivery messages by putting the "c" flag in the recipe, as we did in our earlier recipes.

If you test your recipes with VERBOSE turned off, your log file will only gain the lines not tagged with "procmail:".

% cat ./sample.messages | \   
   /usr/bin/formail -q- -s procmail -tm \ 
   VERBOSE=off ./test-procmailrc
% cat /home/kwm/procmail.logfile
From Fri Nov 17 08:49:31 2000
 Subject: blah
  Folder: secondrecipe.file                            1079
From Fri Nov 17 08:49:52 2000
 Subject: blah
  Folder: /usr/sbin/sendmail -oi    1101
From Fri Nov 17 08:49:31 2000
 Subject: Some random message not directly hit by a recipe
  Folder: /var/spool/mail/kwm                          1166

As we see from the previous command, the actual formail/procmail command had no output when VERBOSE was turned off. Our log file is also fairly compact. For each message, it displays the MTA "From " line (thus the importance of using formail to ensure it was there), the subject of the message, the results of the final delivering recipe, and the size of the message after processing. The first message was filed in "secondrecipe.file" and the second message was forwarded to Since the third message didn't match any recipe at all, it "fell through" and was filed in the default mailbox. Note that the "Folder: " line of the log file, by default, only notes the final delivering action.

If you've used the "c" flag in your recipe file to set up a group of recipes, six of which process a single message, only the last processing recipe (the "delivering" one) will be noted in the log. If you don't like this behavior, you can set successive values to the variable "LOG" in your non-delivering recipes. This will produce ad-hoc output in your log file.

Putting Procmail into Practice

Once you've tested you recipes, put them in an appropriate spot in your personal (usually $HOME/.procmailrc) or system-wide (usually /etc/procmailrc) recipe file and use them in production. If you or your email system administrator are using Procmail as the default delivery agent, you'll only need to put your recipe file in place to have it take effect. If you are not, you can use your .forward file to pipe all your email through Procmail.

You've hopefully learned enough to better manage your email with Procmail. In my next article, we'll build on these fundamentals to do some interesting automation that should appeal to real gearheads.