2026-03-27

Regex matching using previously seen string in Procmail

OK, I was asking Gemini (using Gemini CLI) about a syntax of Procmail rule I wanted to use and it came up with nice and reusable test, so thought I store it here for later as a refference, because I think it can be used nicely to debug any rule.

I'm trying to match emails from Atlasian Jira cloud that contain new comments so I can keep them (and drop almost everithing else). I wanted mine regexp that looks into email body to be bit more specific, because plain commented on is not very uniq string. Gemini continues:


I recently needed to solve a tricky Procmail filtering problem. I wanted to extract a sender's name from a header and use it immediately in a regular expression to match a specific pattern in the email body.

The Question

I have an email like this:

From: "Jane Doe (Jira)" <jira@example.com>
Date: Tue, 24 Mar 2026 03:33:20 +0000
To: John Smith <jsmith@example.com>
Subject: [JIRA] (PROJ-1234) Investigate issue

Jane Doe commented on PROJ-1234:
---------------------------------
Identified the root cause of the crash...

I wanted to take the name from the "From" header and use it in a regex on the body of the email like Jane Doe commented on PROJ-1234:. Is it possible?

The Answer

Yes, this is absolutely possible using Procmail's MATCH variable capture and variable interpolation modifiers. To do this safely—especially when the variable is immediately followed by other text—you use the following pattern:

:0:$PMDIR/notmuch.lock
* ^From: ".* \(Jira\)" <jira@example.com>
* ^From: "\/[^(]+
* $ B ?? $\MATCH()commented on [A-Z0-9]+-[0-9]+:?$
| notmuch insert $tags_keep

The Key Components:

  • The Capture (\/): In the condition * ^From: "\/[^(]+, the \/ token tells Procmail to start capturing whatever matches the rest of the line into the MATCH variable. Here, "[^(]+ captures the name after opening double quotes up to the first parenthesis (e.g., "Jane Doe ").
  • Variable Expansion ($): The $ at the very beginning of the condition line (* $ ...) is critical. It tells Procmail to evaluate variables like $MATCH before processing the regex.
  • Match in email body instead of headers (B ??): The B ?? at the beginning of the condition line (* $ B ?? ...) tells Procmail to look for the pattern in email body instead of headers.
  • Safe Escaping ($\): Using $\MATCH instead of just $MATCH tells Procmail to automatically escape any special regex characters (like . or +) found inside the name so they are treated as literal text.
  • Variable Boundary (()): Since we want to follow the variable immediately with the word "commented", we use (). Just like in Bash where you might use ${MATCH}commented, Procmail needs a boundary. () is an empty regex group that separates the variable name from the following text without affecting the match.

Validation Script

To verify this works as expected, I used the following test script. It creates a mock environment, a sample email, and runs Procmail in verbose mode to confirm the match.

#!/bin/bash

# Setup test environment
TEST_DIR="/tmp/procmail_test"
mkdir -p "$TEST_DIR"
cd "$TEST_DIR"

# 1. Create a sample Jira email
cat << 'EOF' > test.eml
From: "Jane Doe (Jira)" <jira@example.com>
Date: Tue, 24 Mar 2026 03:33:20 +0000
To: John Smith <jsmith@example.com>
Subject: [JIRA] (PROJ-1234) Investigate issue

Jane Doe commented on PROJ-1234:
---------------------------------
Identified the root cause of the crash...
EOF

# 2. Create the Procmail recipe
cat << 'EOF' > test.rc
MAILDIR=/tmp/procmail_test
DEFAULT=/tmp/procmail_test/default
LOGFILE=/tmp/procmail_test/procmail.log
VERBOSE=yes

:0
* ^From: ".* \(Jira\)" <jira@example.com>
* ^From: "\/[^(]+
* $ B ?? $\MATCH()commented on [A-Z0-9]+-[0-9]+:?$
/tmp/procmail_test/matched
EOF

# 3. Run Procmail and check the logs
procmail ./test.rc < test.eml
echo "--- Procmail Log ---"
cat procmail.log
echo "--- Delivery Result ---"
ls -l matched

The verbose log confirms that Procmail correctly expands the variable and matches the body content:

procmail: Match on "^From: "\/[^(]+"
procmail: Match on "()Jane Doe commented on [A-Z0-9]+-[0-9]+:?$"
procmail: Assigning "LASTFOLDER=/tmp/procmail_test/matched"