Fork me on GitHub

Delete a Group of Messages from the Postfix Queue

This article will show you how to delete a group of messages out of a postfix mail queue, from the command line, using a number of standard Linux commands. It is not intended to be a tutorial on Postfix, GAWK, grep, regular expressions, or any other Linux command, but many of them will be covered in a little bit of detail and, at the end, you will be able to add this to your bag of tricks.

Recently I encountered a bit of a problem on the mail server at work. It seems that one of the programmers was testing the final stages of a newly written program that send out HTML newsletters to a group of people. He inadvertently uncommented the line that actually sends the e-mail and put about 1000 messages in my mail queue to fake e-mail addresses. All of the fake e-mail addresses were in the format 'This email address is being protected from spambots. You need JavaScript enabled to view it.' where N is a number between 1 and 9. Unfortunately, there were about 400 other messages in the queue that I wanted to keep so I couldn't dump the entire queue. So, here's what I came up with:

mailq | tail -n +2 | grep -v '^ *(' | \
gawk 'BEGIN {RS = ""} /member[0-9]@domain.com/ {print $1}' | \
tr -d '*!' | sudo postsuper -d -

There is a similar command in the postsuper manual, but it didn't want to work right for me, so I tweaked it and got this. The back slashes in the command allow you to span the command over multiple lines on the Linux system.

Now, let's break this massive command down:

mailq

We all know what mailq does. It prints out the queue. Enough said.

tail -n +2

Tail is a standard command that will print the last 10 lines (or the specified number of lines) of a file. The -n +2 makes tail print the entire file starting from the second line. Since your are piping the output of mailq to tail, you are getting the entire mailq listing without the top line.

grep -v '^ *('

So now we are piping the output of mailq, minus the first line, to grep. The -v option tells grep to invert the match, so it will only display the non-matching lines. The regular expression tells it to find all lines with zero or more spaces at the beginning of the line followed by an open parenthesis. The lines of the mail queue listing that will match that regular expression are status lines and do not contain any information that is needed in further processing.

gawk 'BEGIN { RS = "" } /member[0-9]@domain.com/ { print $1 }'

At this point we have the mail queue without the header line and without any status lines. We need to extract just the message ID numbers out of the queue for the messages that match the ones we want to delete. For this we will use gawk, the GNU version of the awk scripting language. gawk is great for processing text files with records separated by some delemiter.

Once we invoke gawk we give it the command BEGIN {RS = ""}. This blanks out the record separator (which is, by default, a newline character) and causes gawk to treat groups of lines up to a blank line as a single record. In the mail queue listing there is a blank line separating each record. With the status lines removed, we know that the first line of the record contains the queue ID, the size, the time, and the sender's e-mail address. The second lines gives us the receiver's e-mail address. Since those two lines are next to each other, we will treat them as a single record.

The next part of the gawk command is /member[0-9]@domain.com/ {print $1}. This tells gawk to match any records that have that regular expression in them and print the first field of the record. In gawk, each field of a record is referenced by $ followed by the field position in the record. We know that the queue ID is the first field, and we don't need any other fields for the rest of the process, so we only need the first field.

tr -d '*!'

tr is a command that translates or deletes characters. The -d option tells it to delete. the '*!' tells it to delete the asterisks from the end of the queue IDs (if there are any). An asterisk at the end of the queue ID indicates that that message is currently being processed.

sudo postsuper -d -

Now for the action part. postsuper is the Postfix superintendent command. The -d option tells it to delete the following queue ID. To delete a single queue ID you would type something like postsuper -d 3F6252444F8. Since we have a list of queue IDs that we want to feed into postsuper, we give it a - as the queue ID. The dash tells postsuper to read the queue IDs in from the standard input.

Conclusion

There we have it. The queue is all cleaned up. Just for reference, here are a few other fields from the mail queue that can be referenced from gawk:
$1 - Queue ID
$2 - Message Size (in bytes)
$7 - Sender
$8 - Recipient
$9 - Recipient2
Fields 3-6 are the different parts of the time and don't make much sense on their own.