MH のどうでもいいような趣味の講座


「MH 中級者以上向け〜どうでもいい(笑)ような趣味の講座」
 ◎第3章: slocal を張ろう!
● slocal って何?

ネットワークな生活を日々過ごしていると、 仕事関連のメールだけでなく、色々なところから様々なメールが 雨嵐のように降ってくる (苦笑) ようになる (ことが多い^^;;)。 …すると、読む時間もままならなくなったりすることもあって、 届いたメールを開かずにそのままメールスプールディレクトリに残し置いたまま になる…ということになってしまう。 そうなると、 このディレクトリのあるハードディスクのパーティションが 比較的小さく割り当てられているマシンも多いので、 気がつくとそのパーティションを溢れさせてしまい、自分だけでなく、 同じマシン上のユーザさんにも迷惑をかけてしまう…という危険性もない訳ではない。
また、新規メール到着を知らせる xpbiff や、 ~/.cshrc で「set mail=(60 メールスプールファイル名)」などと 一定時間ごとに新規メールをチェックするような設定をしている場合は、 「届いたぞ」「ほらまた届いたぞ」とぴっぴぴっぴ五月蝿いことだろう。
しかも、ようやく溜ったメールを全て inc 出来ても、 +inbox メールフォルダに 色々なところからのメールが何通も何十通も、 下手すると何百通も放り込まれ、そのメールの整理に何時間も費やす羽目になることも ある。

そこで、『来たメールを自分のメールフォルダに自動的に分類して放り込む』 設定が出来るのである。それが、slocal コマンドである。

しかしこの slocal コマンドについての説明は、man が用意されている (MH-6.7 では man mhook、MH-6.8 では man slocal(1))程度で、 あまり詳しい参考資料もなく、 またちょっと書き間違えると、メールが「迷子」になったり、 下手すると「ロスト」したりしてしまう、危険なコマンドでもある。

そこで slocal を設定するにあたって 何らかの役には立つと思うので、 man だけでなく、ソースを読んだり実際に設定してみて確かめたりした 経験を、失敗談や現在の設定とを合わせてここに紹介する。


● slocal の設定

slocal を用いるには、

  1. ~/.forward ファイルの書き換え
  2. ~/.maildelivery ファイルでの設定
の2つの作業が必要である。

○ ~/.forward ファイルの書き換え

~/.forward ファイルは、 個人用のメール受付をカスタマイズできるものであり、 一般的には「そのアドレスに届いたメールを別のところに転送する、その アドレス」を指定するファイルである。 これにパイプ「|」を利用した記述を行なうと shでコマンドを作動させることも可能なのである (参照: vacation コマンド。man vacation(1))

slocal は通常 MH の Lib path にあり、

ことによって、メールフォルダへの振分配送を行なうものである (参照: man mhook/man slocal)。 従って実際には、例えば MH の Lib path が /usr/local/lib/mh であるとすると、 自分の ~/.forward ファイルを
"| /usr/local/lib/mh/slocal -user ユーザ名(=自分のアカウント名)"
設定すれば良い。 これにより届いたメールは、~/.forward で オプション「-user hayashi」付きで起動した /usr/local/lib/mh/slocal の 標準入力に受け渡されることになる。 但し、~/.forward ファイルは 勿論 ~/.maildelivery の設定後に変更すること!!

○ ~/.maildelivery ファイルの設定

このファイルは slocal の動作を制御する設定ファイルであり、 その到着したメールをどう処理するかを決定する。 例えば、

.maildelivery
[リスト3-1] ~/.maildelivery ファイルの例
といった書式で記述する。行頭の「#」はコメント行である。

~/.maildelivery ファイルの各行は、

header pattern action result string
の5つの成分から構成されている。それぞれの意味は man を参照すると、

header
メールヘッダに関する指定フィールドを検索する
source
メールヘッダ行の指定
その中身を 次のpattern とマッチングさせる
"addr"
そのメール中のアドレスと 次のpattern をマッチングさせる
"default"
メールが「配送済」でない (詳細は後述) 場合にマッチする
"*"
常にマッチする
pattern
source に指定したものとマッチングさせる文字列
action
headerpattern に一致した場合、メール配送のために行う動作
"file" もしくは ">"
string で指定するファイルにメールが追加される
尚、ファイルに書込まれる際に「Delivery-Date: date」という、 そのファイルに追加された日時を示す行が付加される
"pipe" もしくは "|"
string に記述したコマンドにメールを標準入力で渡す(pipeする)
そのコマンドは Bourne shellが起動され実行されることに注意
シェルに引き渡される前に、内蔵変数が展開される
$(sender)
メッセージの return address
$(address)
受取人に配送するのに用いられた address
$(size)
メッセージの byte 数
$(reply-to)
メッセージの "Reply-To:" か "From:"
$(info)
miscellaneous out-of-band information
"qpipe" or "^"
pipe に似ているが、シェルに渡さずに内部変数を展開後、 コマンドを直接実行する
quoting special characters をシェルが解釈してしまうのを避けるために 用いる
"destroy"
この動作は常に success する。

尚、MH-6.8 ではこれに加え
"mbox"
"file" と同じであるが、packf で用いられるフォーマット (MMDF mailbox format) でファイルに追加される
また、unofficial なパッチ (MH-6.8.3+-jp2c+ 等)では
"+"
rcvstore を起動する
を使用可能にするものもある。
result
headerpatternにマッチした場合、 実行に対してどのように作動するべきなのかを指定する
尚、~/.maildelivery ファイルは上から順に一つ一つ実行されるが、 最初の 「一致」 をみたところで終了するのでなく、 全ての header vs pattern のマッチングを順に行なう。 従って、複数の pattern にマッチする場合は、 その全ての動作を行なうことになる。
そこで、そのメールに対して「配送済」フラグが用意されており、 このフラグがどういう状態の際に動作を行なうべきかと、 動作完了後、フラグをどう書き換えるかの2点を指定出来る
string
action の指定に対して、メールを追加するファイル名や 引渡す Bourne Shell プログラムなどを記述する
ということである。では上記の ~/.maildelivery ファイル例の各行を 解説してみよう。まず…

# from postmaster
From	postmaster	| A "/usr/local/lib/mh/rcvstore +Pm"
To	postmaster	| A "/usr/local/lib/mh/rcvstore +Pm"
  1. ヘッダの From: 行か To: 行が "postmaster" であったら
  2. パイプで "/usr/local/lib/mh/rcvstore +Pm" に引渡す
  3. 動作に成功すれば「配送済」扱いとする
という手順である。ここでパイプで渡された "/usr/local/lib/mh/rcvstore +Pm" は、Bourne Shell 経由で /usr/local/lib/mh/rcvstore というプログラムを オプション "+Pm" で起動している。
この MH 附属の rcvstore というユーティリティーツールは、 標準入力から読み込んだメールを、 オプション "+フォルダ名" で指定したフォルダに放り込むものである (参照: man rcvstore(1))。 従って、「Pm」というフォルダ (~/.mh_profileで 「Path: Mail」の指定があれば、 実際には ~/Mail/Pm のディレクトリとなる) にメールを放り込むことになる。

# for Maling List
X-Ml-Name	98ml		| A "/usr/local/lib/mh/rcvstore +98"
これも上記と同様であり、「X-Ml-Name: 98ml」というヘッダを持つメールは 「98」というフォルダに放り込むことを指定している。
ML (Mailing List)から来るメールの場合は、 メールヘッダに識別のための特別な「行」がつけてあることが多い。 そこでこれをキーとして検索に一致させ、判別を行なっているのである。

# Personal
From	hal		| ? "/usr/local/lib/mh/rcvstore +Hal"
ここも基本的に同様。「hal」さんから来る個人メールは、From: ヘッダに 「hal」の文字列が含まれているとして、これをキーワードにして検索をしている。
ここで result を "?" にしてあるのは、 上に指定した「98ml」に「hal」さんも加入しているので、 この ML で「hal」さんの出したメールの From: ヘッダに「hal」の 文字列が含まれていると、マッチしてしまう可能性がある。 すると、「98ml」からの「hal」さんのメールが、 98ml のフォルダに入るだけでなく、 Hal というフォルダにも入れられ、 届いた1通のメールが 2つのフォルダに配られることになる。
今回ここの判別では、「hal」さんからの個人メールのみを "Hal" という フォルダに振り分けておきたい…ので、 「98ml からのではない hal さんのメール」という意味で、 「98ml」からメールであるかどうかを先にマッチングして、判別終了後 action "A" で「配送済」フラグを立て、 「hal さんのメールでまだ配送されていないもの」 =「98ml からのではない」と、あとの判別での action に "?" を用いているのである。

# default
default	-		| ? "/usr/local/lib/mh/rcvstore +inbox"
設定に最低限必要なのは、最後のこの1行だけである。 "default" …全てにマッチする、が、action が "?" 指定なので、 「配送済"でないもの"」について、rcvstore +inbox するのである。 つまりここまでの条件判別のどれもにマッチしていない残り全てのメールは、 「inbox」フォルダに放り込む、という設定である。

以上、この ~/.maildelivery ファイルを色々設定して、 上手に slocal を利用すれば、 送られてきたメールに対して様々な action をすることが可能である。

例で説明した以外にも、

  1. メールの差出人等によって「お返事メールを自動的に出す」
  2. メールの差出人等によって「全然読まずに捨ててしまう:-p」
  3. メールフォルダに放り込まずに、ファイルとしてまとめて落す
などなど色々可能である。一つ一つ試して欲しい。

○ ~/.maildelivery ファイルのテスト

前述した様に、この ~/.maildelivery ファイルに誤った記述をして しまうと、メールがロストしてしまう可能性も多い。 テストの間は、

  1. コマンドラインから実際にメールファイルを slocal に食わせてみる
    % cat メールファイル | $MHLIB/slocal -verbose -debug -user $USER
    
    としてテストする。verbose オプションをつけることでより詳細な情報が帰って来る

  2. ~/.forward で slocal 起動時に verbose/debug オプションを立ててみる
    ~/.forward ファイルを、
    "|/bin/sh -c 'exec >> /tmp/out 2>&1; $MHLIB/slocal -user $USER -verbose -debug'"
    
    とし、オプションに対するログを /tmp/out に落すように設定する

  3. ~/.maildelivery ファイルで rcvstore に食わせる時にログを吐かせてみる
    ~/.maildelivery ファイルで、
    To hogehoge | R "set -xv; exec >/tmp/out 2>&1; $MHLIB/rcvstore +hogehoge"
    
    とし、 rcvstore に食わせる際のログを落してみる

など、色々工夫してテストするとよいであろう (参照 MH FAQ 07.05)。

また、slocal を使用しはじめてからしばらくの間は、誤動作等ないかの確認のために ~/.forwardを

\hayashi, "| /usr/local/lib/mh/slocal -user hayashi"
の様に MH (slocal) に喰わすだけでなく、 通常配送も行なうようにしておくとよい であろう。

○ 余談

「~/.forward で プログラム(slocal) にメールを喰わせている」ということは、 sendmail.cf 中のローカル配送指定の Mlocal 行 (通常 /bin/mail) で なくて、Mprog の行 (通常 /bin/sh) が使用されているのである。

sendmail.cf
[リスト3-2] sendmail.cf の例 (一部)
例えば、届いたメールを JIS から EUC に変換しているサイト等では、 ローカル配送の際に 漢字コード変換 filter を 噛ませていることが多いが、 slocal を使用しつつ、 そういう操作を 必要とする場合は、 なんらかのフィルターを自分で slocal の前に噛ます様に工夫する必要が あるだろう。


● slocal の使い (?) 方

以上で slocal が使えるようになった。
届けられたメールは自動的に分類され、 自分の home directory のメールフォルダに放り込まれる。 これで、/var/spool/mail 等の個人メールスプールファイルは、常に 0 のまま となるので、 そのディレクトリの容量を気にしなくてもよいのだが、 今度は自分のディレクトリが "知らぬ間に" 増えていくので、 常に充分気をつけておく必要が当然ある。

さて、自動的に届けられたのはいいものの、どうやって「メールが届いた」ことを 知ることが出来るだろうか?

それは、前述の ~/.mh_profile に設定した 「Unseen-Sequence: unseen」がここで効いてくるのである。 これを設定すると、各フォルダの .mh_sequences に、

unseen: 1-6
というように "未開封" のメールを記録してくれる。 従って、これを grep してやればよい。

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- um.sh

#!/bin/csh -f
#
#	um.sh -- scaning Unseen Mails.
#	   Original: by Haruhisa Hayashi 
#	   Modified: by Katsumi Ohta 
#
onintr brk

# public setting
set fname_pth=`mhpath +`
set fname_dir=`folders -fast`
set fname_seq=.mh_sequences
set seq_name=`grep Unseen-Sequence: $HOME/.mh_profile | sed -e 's/^.*: //'`
#
set mhform_inbox="unseen2.form"
set mhform_other="unseen1.form"

@ unseen_num = 0

foreach folder ($fname_dir)
    grep $seq_name $fname_pth/$folder/$fname_seq >& /dev/null
    if ($status == 0) then
	echo "<< Unseen Mail >> in +"$folder
	if ($folder == "inbox") then
	    set mhform = $mhform_inbox
	else
	    set mhform = $mhform_other
	endif
	scan +$folder $seq_name -form $mhform | tee -a /tmp/scan.$$
    endif
end

if (-f /tmp/scan.$$) set unseen_num = `wc -l < /tmp/scan.$$`

switch ($unseen_num)
    case 0:
        echo " You've already seen all of your mail."
        breaksw
    case 1:
        echo " You have only one unseen mail."
        breaksw
    default:
        echo " You have $unseen_num unseen mails."
	breaksw
endsw
brk:
rm -f /tmp/scan.$$
exit 0;
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

この um.sh は、私が書き殴った(笑)ものを、 友人が汎用に書き直してくれたシェルプログラムである (→ um.sh を get )。 未開封メールが、どのフォルダに何通あるか表示してくれる。

set fname_pth=`mhpath +`
MH の mail パス (~/.mh_profile の「Path:」) を返す "mhpath +" コマンドを利用し、環境変数 fname_pth にセット
set fname_dir=`folders -fast`
フォルダ名一覧を返す folders コマンドを使用し、 フォルダのリストを環境変数 fname_dir にセット
set seq_name=`grep Unseen-Sequence: $HOME/.mh_profile | sed -e 's/^.*: //'`
~/.mh_profile の「Unseen-Sequence:」に何を指定したかを grep で取得 し、環境変数 seq_name にセット
これらの情報を用いて、フォルダ一つ一つに grep を行なう、 という C-Shell スクリプトである。

但し、この様に汎用性を持たせると、どうしても実行速度が遅くなるので、 最初から"判っている"場合や、"このフォルダだけを調べたい"などという場合には、 例えば、

# private setting
set fname_pth=/home/hayashi/Mail
set fname_dir=( inbox 98ml Hal Postmaster )
set seq_name=unseen
などと、直接これらの環境変数に与えておくとよい。
あとは、grep により未開封のメール番号を取得し、 それらに対して scan して内容を表示するのであるが、 inbox を scan する時と、そうでない時で 使用する MH フォーマットファイルを unseen2.form と unseen1.form のそれぞれに変更するようにしている。 勿論、これは私の趣味:-)で区別しているだけであるので、 同じものを使用しても問題はないし、自分でさらに拡張することも 可能である。

尚、その後これは更に別の友人の手によって perl スクリプトに書き直された。 この方が少し速い (→ um.pl を get )。

考え方は、上述の um.sh と同様に、

  1. ~/.mh_profile の 「Path: 」に指定された ディレクトリの下を全部掘ってメールフォルダを探す
  2. それぞれのフォルダについて .mh_sequence から unseen を grep で探す
  3. inbox フォルダ/他のフォルダで区別して、MH フォーマットファイルを 変更して scan で表示する
ことを行なっている。
やはりこれも um.sh と同じく、汎用性を持たせると実行速度が遅くなるので、 最初から判っているものは、直接与えておくほうが速い。 リスト中の $ignores で検索しないメールフォルダをしていしておく、もしくは public setting を全部コメントアウトして @folders に、調べたいメールフォルダを全部記述しておくなどとする。

これによって、届けられたメールは自動的に分配され、 必要な時にどんなメールが届いているのかを知ることが出来るようになる。 勿論、~/.maildelivery に色々と仕掛け、C-Shell, Perl, Tcl/tk 等のシェルや X のクライアントプログラムを作成し組み合わせることで、 届いた時点で「届いたぞ」と PopUp して通告するなどのシステムも 不可能ではない。 色々と挑戦して貰いたい


○ さらに余談

ちなみに「slocal のファイルロック周りは "甘い" ので、 一度に大量のメールが来ると、メールがロストすることもある」 と言われている。
但し残念(?)乍ら、 1日100通程度届くことも多い私個人としては、 今までに一度も経験したことはない。 ちゃんと mail のログ (smtp connection) を tail -f で常時見てるので、 気がつかずにロストしている…ということはない筈。 私的には、MH のコンパイル時に指定する LOCK 関数と、 OS自体の flock/lockf 周りの問題じゃないかと思っているが…。

あと、メールが届く度に Unseen-Sequences を更新するので、 そのメールフォルダの .mh_sequence が勝手に:-)変更される。 これが未読メールを丁度読んでいたりすると、変にぶつかったりすることも 稀にないわけではないので注意。


© Haruhisa Hayashi 1992,1997
気まぐれにより個々のファイル名を変えてしまう可能性もありますので、 Link/Bookmark される場合は、 %7Ehayashi/internet/mh_guide.html にお願いします。
目次へ 第0章へ 第1章へ 第2章へ 第3章へ
直接このファイルに飛んで来た場合… フレーム版メニューへ
by はやし はるひさ hayashi あっとまーく laic.u-hyogo.学術.日本