PROCMAILSC(5)

๐Ÿ“ฆ
version 3.24 ยท 3 versions
, in centos-stream 10, fedora 43, fedora 44, nixos 26.05, nixpkgs unstable, opensuse-leap 16.0, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

๐Ÿ’– PROCMAILSC ๐Ÿ’–

okay bestie, this is procmailsc from the File Formats Manual, and it's literally the glow-up your recipes have been waiting for. it's the procmail weighted scoring technique โ€” and no, it's not boring, it's giving โœจmathโœจ and it slays.


๐ŸŒธ NAME ๐ŸŒธ

procmailsc โ€” the procmail weighted scoring technique. that's it, that's the name, and it's iconic.


โœจ SYNOPSIS โœจ

[*] w^x condition

๐Ÿ’… DESCRIPTION ๐Ÿ’…

so like, you already know the classic true-or-false conditions you can put on a recipe, right? well, plot twist: you can also use a weighted scoring technique to decide whether a recipe matches or not. when weighted scoring is in play, the final score for that recipe has to be positive for it to match. no positive score, no match. periodt. ๐Ÿ’€

a condition can chip in to the score if you give it a weight (w) and an exponent (x). you do that by sticking this right in front of the condition, on the same line:

w^x

and both w and x are real numbers from -2147483647.0 to 2147483647.0 inclusive. yes, inclusive. we love an inclusive queen. ๐Ÿ‘‘


๐Ÿ”ฅ Weighted regular expression conditions ๐Ÿ”ฅ

the first time the regular expression is found, it adds w to the score. the second time it's found, w*x gets added. third time? w*x*x. fourth time, w*x*x*x. and so on and so on, forever, exponential queen behavior. ๐Ÿ’–

here's the concise formula, look at her:

                                   n
                   n   k-1        x - 1
              w * Sum x    = w * -------
                  k=1             x - 1

that's the total added score for this condition if n matches are found. math ate, honestly.

and now the case distinctions, because these matter, no cap:

  • x=0 โ€” only the first match contributes w to the score. any matches after that? ignored. not even looked at. bye. ๐Ÿ™ˆ
  • x=1 โ€” every match contributes the same w to the score. the score grows linearly with the number of matches found.
  • 0<x<1 โ€” every match contributes less than the one before it. the score asymptotically approaches a certain value (go peek at the NOTES section below, it's all there).
  • 1<x โ€” every match contributes more than the one before it. the score grows exponentially. she's escalating. ๐Ÿ”ฅ
  • x<0 โ€” can be utilised to favour an odd or even number of matches. yes, really.

and if the regular expression is negated (i.e., it matches when it isn't found), then n obviously can only ever be zero or one. simple as that. ๐Ÿฆ‹


๐Ÿ’œ Weighted program conditions ๐Ÿ’œ

if the program returns an exitcode of EXIT_SUCCESS (=0), then the total added score will be w. if it returns literally any other exitcode (which means failure), the total added score will be x.

and if the exitcode of the program is negated, then the exitcode gets treated as if it were a virtual number of matches. the calculation of the added score then proceeds exactly as if it had been a normal regular expression with n= exitcode matches. mind = blown. ๐Ÿงš


๐ŸŒˆ Weighted length conditions ๐ŸŒˆ

if the length of the actual mail is M, then:

* w^x  > L

will generate an additional score of:

                         x
                  /  M  \
              w * | --- |
                  \  L  /

and:

* w^x  < L

will generate an additional score of:

                         x
                  /  L  \
              w * | --- |
                  \  M  /

in both cases, if L=M, this adds w to the score. but here's the tea: in the former case, larger mails get favoured. in the latter case, smaller mails get favoured. and even though you can vary x to fine-tune the steepness of the function, typical usage just sets x=1. keep it simple, bestie. ๐Ÿ’…


๐ŸŽ€ MISCELLANEOUS ๐ŸŽ€

you can query the final score of all the conditions on a recipe from the environment variable $=. this variable gets set every single time, just after procmail has parsed all conditions on a recipe โ€” even if the recipe is not being executed. she's always reporting back to you, that's loyalty. ๐Ÿซถ


โญ EXAMPLES โญ

okay this is the part you actually came for. let's go.

the following recipe will ditch all mails having more than 150 lines in the body. the first condition contains an empty regular expression which, because it always matches, is used to give our score a negative offset. the second condition then matches every line in the mail, and consumes up the previous negative offset we gave (one point per line). in the end, the score will only be positive if the mail contained more than 150 lines. chef's kiss. ๐Ÿ˜ญ

:0 Bh
* -150^0
*    1^1  ^.*$
/dev/null

now suppose you have a priority folder which you always read first. the next recipe picks out the priority mail and files them in this special folder. the first condition is a regular one, i.e., it doesn't contribute to the score, but simply has to be satisfied. the other conditions describe things like: john and claire usually have something important to say, meetings are usually important, replies are favoured a bit, mails about Elvis (this is merely an example :-) are favoured (the more he is mentioned, the more the mail is favoured, but the maximum extra score due to Elvis will be 4000, no matter how often he is mentioned), lots of quoted lines are disliked, smileys are appreciated (the score for those will reach a maximum of 3500), those three people usually don't send interesting mails, the mails should preferably be small (e.g., 2000 bytes long mails will score -100, 4000 bytes long mails do -800). as you see, if some of the uninteresting people send mail, then the mail still has a chance of landing in the priority folder, e.g., if it is about a meeting, or if it contains at least two smileys. ๐Ÿ‘‘

:0 HB
*         !^Precedence:.*(junk|bulk)
* 2000^0   ^From:.*(john@home|claire@work)
* 2000^0   ^Subject:.*meeting
*  300^0   ^Subject:.*Re:
* 1000^.75 elvis|presley
* -100^1   ^>
*  350^.9  :-\)
* -500^0   ^From:.*(boss|jane|henry)@work
* -100^3   > 2000
priority_folder

and if you are subscribed to a mailinglist, and just would like to read the quality mails, then the following recipes could do the trick. first we make sure that the mail is coming from the mailinglist. then we check if it is from certain persons of whom we value the opinion, or about a subject we absolutely want to know everything about. if it is, file it. otherwise, check if the ratio of quoted lines to original lines is at most 1:2. if it exceeds that, ditch the mail. everything that survived the previous test, is filed. ๐Ÿฆ‹

:0
^From mailinglist-request@some.where
{
  :0:
  * ^(From:.*(paula|bill)|Subject:.*skiing)
  mailinglist

  :0 Bh
  *  20^1 ^>
  * -10^1 ^[^>]
  /dev/null

  :0:
  mailinglist
}

for further examples you should look in the procmailex(5) man page. go, read it, it's good for you. ๐Ÿ’–


๐Ÿ’€ CAVEATS ๐Ÿ’€

listen, this part matters, don't skip it. because this speeds up the search by an order of magnitude, the procmail internal egrep will always search for the leftmost shortest match โ€” unless it is determining what to assign to MATCH, in which case it searches the leftmost longest match.

e.g., for the leftmost shortest match, by itself, the regular expression:

  • .* โ€” will always match a zero length string at the same spot.
  • .+ โ€” will always match one character (except newlines of course).
bestie this trips everyone up. leftmost shortest is the default, leftmost longest is only for MATCH. memorize it. ๐Ÿง 

๐ŸŒŸ SEE ALSO ๐ŸŒŸ

procmail(1), procmailrc(5), procmailex(5), sh(1), csh(1), egrep(1), grep(1),


๐Ÿ™ˆ BUGS ๐Ÿ™ˆ

if, in a length condition, you specify an x that causes an overflow, procmail is at the mercy of the pow(3) function in your mathematical library. like, completely at its mercy. no backup plan. ๐Ÿ˜ญ

also: floating point numbers in engineering format (e.g., 12e5) are not accepted. don't try it. she said no.


๐Ÿ’– MISCELLANEOUS (part two, the sequel) ๐Ÿ’–

as soon as plus infinity (2147483647) is reached, any subsequent weighted conditions will simply be skipped.

as soon as minus infinity (-2147483647) is reached, the condition will be considered as no match and the recipe will terminate early.

so yeah, the score has limits, and hitting them changes everything. dramatic? maybe. correct? absolutely. ๐Ÿ’…

๐Ÿ”ฎ NOTES ๐Ÿ”ฎ

if in a regular expression weighted formula 0<x<1, the total added score for this condition will asymptotically approach:

                 w
              -------
               1 - x

and in order to reach half the maximum value, you need:

                   - ln 2
              n = --------
                     ln x

matches. there you go, now you can calculate exactly how much is too much. math bestie behavior. ๐Ÿงฎโœจ


๐Ÿ‘‘ AUTHORS ๐Ÿ‘‘

Stephen R. van den Berg <srb@cuci.nl>


and that's the whole page, bestie! this one was written by BuGless and it's from the PROCMAILSC(5) manual. go forth and score your mail like the main character you are. ๐Ÿ’–โœจ๐ŸŽ€๐Ÿฆ‹๐ŸŒธ๐Ÿ’…๐Ÿซถ