PROCMAILEX(5)

名前
procmailex - procmail 設定ファイルの例

書式
$HOME/.procmailrc の設定例

説明
設定ファイル (rcfile) の書式の説明は procmailrc(5) を参照のこと。

重みつきスコアリング手法については procmailsc(5) に詳しい説明がある。

この manpage は、いろいろなレシピの例を示したものである。 rcfile 全体の例については、 procmail(1) の 備考 の節や、procmail のソース配布に含まれる rcfile のサンプル (procmail*/examples/?procmailrc) を参照してほしい。

例
scuba-dive メーリングリストからのメールをすべて選び出して scubafile と いうメールフォルダに入れる (ロックファイルとして scubafile.lock を使用する)。

    :0: * ^TOscuba scubafile

peter からのメールで、Subject に compilers が入っているものを すべて william に転送する (かつメールのコピーを petcompil に保存する)。

    :0 * ^From.*peter * ^Subject:.*compilers {
       :0 c
       ! william@somewhere.edu

       :0
       petcompil }

上記と同じことを実現する別の方法:

    :0 c * ^From.*peter * ^Subject:.*compilers ! william@somewhere.edu

       :0 A
       petcompil

同じことを実現できるが、上記の方法より少し動作が遅い方法:

    :0 c * ^From.*peter * ^Subject:.*compilers ! william@somewhere.edu

    :0 * ^From.*peter * ^Subject:.*compilers petcompil

procmail を使うのがほとんど初めてで、ちょっとだけ試してみたい場合は、 何らかの セーフティネット を設けておくと助かることが多い。 以下の 2 つのレシピを、他のどのレシピより前に入れておくと、 到着したメールのうち常に最新の 32 通を保存することができる。 この機能を働かせるためには、これら 2 つのレシピを追加する前に $MAILDIR に `backup' という名前のディレクトリを作っておかなければならない。

    :0 c backup

    :0 ic | cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`

使っているメールシステムが、各メールの先頭の `From ' 行を生成しなかったり、 間違った `From ' 行を生成したりする場合は、procmail を -f- オプションをつけ て呼び出すことでこの問題を修正する (すなわち、正しい `From ' 行をつける) ことができる。 この問題を解決する別の方法としては、以下のレシピを rcfile の中で一番最初の レシピとして入れる方法もある。こうすると、全てのメールのヘッダは formail に よりフィルタリングされる。 formail は、メールの先頭にある `From ' 行を 取り除いた上で、さらに `From ' 行を自動生成する。

    :0 fhw | formail -I "From " -a "From "

管理者 (postmaster) 以外から来た全てのメールのヘッダを、(統計をとったり、 メールのデバッグのために) 自分だけのヘッダコレクションに追加するには、 次のように設定する。書きこみ時のロックファイルとして `headc.lock' を 使用する。パイプ処理が完了する前にロックファイルが削除されることがないよ うに、`w' オプションを指定しなければいけない。指定しなかった場合、 パイプが処理対象のメールを受け付けるとすぐに ロックファイルは削除されてしまう。

    :0 hwc: * !^FROM_MAILER | uncompress headc.Z; cat >>headc; compress headc

上記のレシピで compress の代わりに、もっと圧縮率が良い gzip を使用するには 以下のようにすればよい。

    :0 hwc: * !^FROM_MAILER | gzip >>headc.gz

1000 バイト未満のメールをすべて自宅のアドレスに転送する (このレシピの場合、ロックファイルは必要ない)。

    :0 * < 1000 ! myname@home

surfing メーリングリストのまとめ送りのメールを個々のメッセージに分割して、 メールフォルダ surfing に格納する。ロックファイルとして surfing.lock を使用する。

    :0: * ^Subject:.*surfing.*Digest | formail +1 -ds >>surfing

postmaster や mailer-daemon から来た全てのメール(エラーで戻ってきたメールなど) を postm ファイルに格納する。ロックファイルとして postm.lock を使用する。

    :0: * ^FROM_MAILER postm

簡単な自動返信用のレシピ。このレシピは、デーモンからのメール (戻ってきた メールやメーリングリストからのメールなど) や自分自身からの自動返信のメールには 自動返信しないことを保証している。もしこのような注意を怠ったならば、 (メール ループなど) 大変なことが起こり得る。 このレシピを用いて、受信したすべてのメールに対して自動応答を行うためには、 言うまでもないが、 rcfile の他の全てのレシピより前にこれを置く必要がある。 実際には、講読しているメーリングリストからのメールを処理するレシピの 後ろ にこのレシピを置くことを推奨する。一般的には、 メーリングリストに対して自動返信を行うのはよい考えではない。 (確かに、正規表現 !^FROM_DAEMON によってそのようなメールはすでに捕まえ られているはずだが、メーリングリストが一般的な慣習に従っていない場合は、 これだけでは 不十分 だからである。)

    :0 h c * !^FROM_DAEMON * !^X-Loop: your@own.mail.address | (formail -r -I"Precedence: junk" \
        -A"X-Loop: your@own.mail.address" ; \
       echo "Mail received.") | $SENDMAIL -t

以下はもっと複雑な自動返信のレシピの例で、有名な vacation(1) プログラムと同じ機能を実現するものである。 このレシピは (メールループを防ぐなどの) 直前のレシピと同じ方針で作られている。 さらに、このレシピでは送信者の名前を抽出して vacation データベースを作っており、 名前が新しいものだったときには vacation.cache ファイルにその名前が追加 される (vacation.cache ファイルは formail により管理されており、 常に最近の送信者名が格納され、ファイルのサイズの上限が約 8192 バイトに なることが formail により保証される)。 新しい送信者名だった場合は、自動応答メッセージが送信される。

見て分かるように、以下のレシピでは条件の「間」にコメントが 入っている。このようなコメントの入れ方は認められている。 しかし、条件と同じ行にコメントを入れては「いけない」。

    SHELL=/bin/sh # for other shells, this might need adjustment

    :0 Whc: vacation.lock
     # Perform a quick check to see if the mail was addressed to us * $^To:.*\<$\LOGNAME\>
     # Don't reply to daemons and mailinglists * !^FROM_DAEMON
     # Mail loops are evil * !^X-Loop: your@own.mail.address | formail -rD 8192 vacation.cache

      :0 ehc # if the name was not in the cache
      | (formail -rI"Precedence: junk" \
            -A"X-Loop: your@own.mail.address" ; \
          echo "I received your mail,"; \
          echo "but I won't be back until Monday."; \
          echo "-- "; cat $HOME/.signature \
         ) | $SENDMAIL -oi -t

TeX に関係する全メッセージを、texmail というディレクトリに、一通毎に別の、 他とは重複しない名前のファイルに保存する (ここで指定するディレクトリは あらかじめ存在しなければならない)。 この場合、ロックファイルを使用する必要がないので、 レシピでもそうなっている。

    :0 * (^TO|^Subject:.*)TeX[^t] texmail

上と同じだが、メールを番号が振られたファイル (MH 形式のフォルダ) に格納する点が異なる。

    :0 * (^TO|^Subject:.*)TeX[^t] texmail/.

メールを同時に複数のディレクトリ・フォルダに振り分けることもできる。 以下のレシピは、メールを 2 つの MH 形式のフォルダと 1 つのディレクトリ・ フォルダに振り分ける。実際にはファイルは 1 つ作成されるだけで、追加で ハードリンクが 2 つ作成される。

    :0 * (^TO|^Subject:.*)TeX[^t] texmail/. wordprocessing dtp/.

会議 (meeting) に関する全メッセージを月毎に異なるディレクトリに 保存する。例えば、1994 年 1 月だったとすると、フォルダ名は `94-01/meeting' という名前となり、ローカルロックファイルは `94-01/meeting.lock' になる。

    :0: * meeting `date +%y-%m`/meeting

上と同じだが、`94-01' ディレクトリが存在しなかった場合、 自動的に作成される。

    MONTHFOLDER=`date +%y-%m`

    :0 Wic * ? test ! -d $MONTHFOLDER | mkdir $MONTHFOLDER

    :0: * meeting ${MONTHFOLDER}/meeting

上と同じだが、少しだけ違う方法:

    MONTHFOLDER=`date +%y-%m` DUMMY=`test -d $MONTHFOLDER || mkdir $MONTHFOLDER`

    :0: * meeting ${MONTHFOLDER}/meeting

複数のメーリングリストを講読していて、そのメーリングリストのいくつかに クロスポストする人がいる場合、同じメッセージを何回か受け取ることがよくある (各メーリングリストからは一通だが)。以下の簡単なレシピを使うと、重複した メールを除去することができる。このレシピでは、 formail を通じて最近受信した メールの Message-ID を格納する 8KB のキャッシュ・ファイルを作っている。 Message-ID は新着メール毎に一意であることが保証されているので、Message-ID を使うのは重複するメールを除くのにまさにぴったりの方法である。このレシピを rcfile の一番最初に置くだけで、重複するメールはこのレシピを通過できなくなる。

    :0 Wh: msgid.lock | formail -D 8192 msgid.cache

注意すべきこととして、これより後のレシピで配信に問題があると procmail は そのメールをキューにもう一度入れようとする。そうすると、次回のキュー処理の 際にこのメールは重複しているとみなされ、なくなってしまう。 自分のスクリプト作成技術にあまり自信がない場合は、代わりに以下のレシピを 使うことができる。このレシピは、重複するメールをどこか捨ててしまうのではなく、 別のフォルダに入れる。もちろん、定期的にこのフォルダを空にするのは、 自分でやらないといけない。

    :0 Whc: msgid.lock | formail -D 8192 msgid.cache

    :0 a: duplicates

procmail は MH 形式のフォルダに直接メッセージを配信することができるが、 本物の MH が管理している未読番号 (unseen sequence) の更新は行わない。 procmail にも未読番号の更新を行わせたい場合は、以下のようなレシピを使うとよい。 この例では、本文に spam という言葉を含む全てのメールを spamfold という MH フォルダに入れている。ローカルロックファイルが必要な点に注意すること。 なぜなら、MH のプログラム群は番号ファイル (sequences file) のロックを 行わないからである。したがって、番号ファイルを変更する MH のプログラムが 非同期で複数実行されると、内容がおかしくなったり、いつの間にか変更が失われる 可能性がある。残念ながら、ロックファイルを使ってもこの問題を完全に解決する ことはできない。`show'、`mark' や他の MH プログラムの実行中に rcvstore が起動されることがあり得るからである。この問題が MH の将来のバージョンで 修正されることを期待しているが、それまでは、番号が失なわれたりおかしく なったりする危険と、未読番号を使う利点を、天秤にかけた上で使わなけらば ならないだろう。

    :0 :spamfold/$LOCKEXT * B ?? spam | rcvstore +spamfold

emacs のメールフォルダ (RMAIL や VM といった emacs 上で動作するメール・ パッケージで管理されているメールフォルダ) に直接配信する場合、 emacs と 同じロックファイルを使用すべきである。 emacs のメーラーはこの点で少し おかしなところがあり、すでに emacs の内部バッファに読み込まれている メールフォルダに第三者がメールを配信すると、非常に混乱してしまう。 以下のレシピは $HOME の値が /home/john の場合の例である。

    MAILDIR=Mail

    :0:/usr/local/lib/emacs/lock/!home!john!Mail!mailbox * ^Subject:.*whatever mailbox

別の方法としては、procmail 専用のメールボックスを設け、procmail はそこに メールを配信するようにして、定期的に movemail を使ってメールボックスを空にし、内容を emacs メーラーのファイルにコピーする こともできる。 movemail はメールボックス単位のローカルロックファイル mailbox.lock を使用する。 実のところ、procmail と一緒に使う場合、この方法を使う方が望ましい。

メールから特定のヘッダを抽出して環境変数に設定するには、 以下のいずれかの方法も使用することができる。

    SUBJECT=`formail -xSubject:` # 通常のフィールド FROM=`formail -rt -xTo:` # 特殊な例

    :0 h # 他の方法 KEYWORDS=| formail -xKeywords:

procmailrc ファイル内で一時ファイルを使っていて、procmail 終了直前に 一時ファイルが確実に削除されるようにするには、以下のようにすればよい。

    TEMPORARY=$HOME/tmp/pmail.$$ TRAP="/bin/rm -f $TEMPORARY"

キーワード TRAP は procmail の終了コードを変更するために使うこともできる。 つまり、通常の終了コードの代わりに終了コード `1' を procmail が返すように したい場合、以下のようにすればよい:

    EXITCODE="" TRAP="exit 1;" # 最後のセミコロンは大事である。 exit は独立した
                     # コマンドではなくシェルのコマンドだからである。

終了コードに TRAP から起動されたプログラムの結果を反映させる必要がない 場合は、以下のように書くだけでもよい。

    EXITCODE=1

以下のレシピは、受信したメールのうち postscript ファイルと思われるメールを 全部印刷する。

    :0 Bb * ^^%! | lpr

次のレシピは同じ動作をするが、もう少し選択条件が厳しくなっている。 print-server から来た postscript ファイルだけを印刷する。 最初の条件は、ヘッダ内に指定パタンが見つかった場合のみマッチする。 二番目の条件は、メール本文の先頭でのみマッチする。

    :0 b * ^From[ :].*print-server * B ?? ^^%! | lpr

上と同じだが、少しだけ違う方法:

    :0 * ^From[ :].*print-server {
      :0 B b
      * ^^%!
      | lpr }

これも同様:

    :0 HB b * ^^(.+$)*From[ :].*print-server * ^^(.+$)*^%! | lpr

メールアカウントを 2つ持っているとしよう。どちらも常時使用しているが、 とても離れた場所にあるとする (つまり、いずれか一方のアカウントに届いた メールしか読むことができないとする)。アカウント 1 に届いたメールを アカウント 2 に転送し、反対向きも行いたいという状況を考える。 まず思いつくのは、両方のサイトで .forward ファイルを使う方法だが、 もちろんうまく動かない。メールのループが起こってしまうからだ。 このメールのループは、両方のサイトの $HOME/.procmailrc ファイルの先頭に 以下のレシピを入れておくことで避けることができる。 両方のサイトで同じ X-Loop: フィールドを追加するようにしておけば、 メールはどちらのアカウントからでも安全に もう一方のアカウントに転送することができるようになる。

    :0 c * !^X-Loop: yourname@your.main.mail.address | formail -A "X-Loop: yourname@your.main.mail.address" | \
       $SENDMAIL -oi yourname@the.other.account

誰かが件名に `retrieve' というキーワードを入れたメールを送ってくると、 以下のレシピは自動的に info_file の内容を送信者に送り返す。 メールを送信するレシピではいつもそうであるが、メールのループが起こらない ように気を付けること。

    :0 * !^From +YOUR_USERNAME * !^Subject:.*Re: * !^FROM_DAEMON * ^Subject:.*retrieve | (formail -r ; cat info_file) | $SENDMAIL -oi -t

今度は、メールでアクセス可能な非常に簡単なファイルサーバの例である。 もっと高度な機能が必要な場合には、 SmartList を調べてみることをお薦めする (SmartList は procmail の配布場所と同じ場所で入手できる)。 以下のリストにあるように、このファイルサーバは一つのリクエストに対し 最大でも一つのファイルしか送り返さない。また、受信したメールの本文は無視され、 Subject: 行は「Subject: send file the_file_you_want」 (空白には意味がある) という形式になっていなければならない。 ファイルネームがドット (.) で始まるファイルは送り返さないし、 ファイルサーバのディレクトリ・ツリーの外部にあるファイルを 取り出すこともできないようになっている (この例に手を入れる場合は、この最後の制限項目を不注意で 緩めることがないように気をつけること)。

    :0 * ^Subject: send file [0-9a-z] * !^X-Loop: yourname@your.main.mail.address * !^Subject:.*Re: * !^FROM_DAEMON * !^Subject: send file .*[/.]\. {
      MAILDIR=$HOME/fileserver # ファイルサーバのディレクトリに移動

      :0 fhw # 返信用ヘッダ作成とファイル名抽出
      * ^Subject: send file \/[^ ]*
      | formail -rA "X-Loop: yourname@your.main.mail.address"

      FILE="$MATCH" # 要求のあったファイル名

      :0 ah
      | cat - ./$FILE 2>&1 | $SENDMAIL -oi -t }

以下の例は、特定の MIME 形式でエンコードされて届いた plain-text のメールを、よりコンパクトな 8 ビット形式に前もって変換する。 こうすることで、多くのプログラムで簡単に使用したり表示したり できるようになる。 mimencode(1) プログラムは Nathaniel Borenstein の metamail パッケージに含まれている。

    :0 * ^Content-Type: *text/plain {
      :0 fbw
      * ^Content-Transfer-Encoding: *quoted-printable
      | mimencode -u -q

         :0 Afhw
         | formail -I "Content-Transfer-Encoding: 8bit"

      :0 fbw
      * ^Content-Transfer-Encoding: *base64
      | mimencode -u -b

         :0 Afhw
         | formail -I "Content-Transfer-Encoding: 8bit" }

以下の例はあまり見かけないものだが、こんな機能もこともできるという 紹介である。HOME ディレクトリに ".urgent" という名前のファイルがあり、 そのファイルに (一人の) 人の名前が書かれていて、その人からメールが来た場合、 通常のメールフォルダに入れる代わりに $MAILDIR/urgent に格納したいとする。 このレシピでそれができる。 (気を付けるべき点として、$HOME/.urgent のファイルサイズは $LINEBUF より 小さくすべきである。必要なら LINEBUF の値を増やすこと。)

    URGMATCH=`cat $HOME/.urgent`

    :0: * $^From.*${URGMATCH} urgent

procmail の全く別の使い方として、特定の (外に出ていく) テキストやメールに 条件に応じてフィルタを適用するといった方法がある。代表的な例としては、 外に出ていく全てのメールを通すフィルタで、 必要な場合のみ MIME エンコードされるようにするものがある。 この場合には、パイプの中段として procmail を起動することになる。

    cat newtext | procmail ./mimeconvert | mail chris@where.ever

mimeconvert rcfile には、以下のようなルールが入ることだろう (=0x80= や =0xff= には 実際の 8 ビット文字を入れること):

    DEFAULT=| # いつものようにメールを配信するのではなく
                 # 標準出力にパイプする :0 Bfbw * [=0x80=-=0xff=] | mimencode -q

      :0 Afhw
      | formail -I 'MIME-Version: 1.0' \
         -I 'Content-Type: text/plain; charset=ISO-8859-1' \
         -I 'Content-Transfer-Encoding: quoted-printable'

関連項目
procmail(1), procmailrc(5), procmailsc(5), sh(1), csh(1), mail(1), mailx(1), binmail(1), uucp(1), aliases(5), sendmail(8), egrep(1), grep(1), biff(1), comsat(8), mimencode(1), lockfile(1), formail(1)

著者
Stephen R. van den Berg
    <srb@cuci.nl>
Philip A. Guenther
    <guenther@sendmail.com>