Edited at

シェルスクリプトでメール送信、「さようならnkf」編

More than 3 years have passed since last update.

プログラムからメールを送信したいなーと思ったことありますよね。そんな時、どうやってますか?

何らかのプログラミング言語でメール送信用のライブラリーの使い方調べて組む?

いやいや、そんなことしなくてもシェルスクリプト(と基本的なコマンド)で送れますって。


必要なのはたったこれだけ


  • メールを送信できるホスト (これ最重要)

  • Bourneシェル(素のshでも、bashでもdashでもkshでもzshでも)

  • sendmailコマンド(大抵は /usr/sbin あたりに入ってます)

これだけあればOK。つまりPOSIX(UNIXで必須のもの)以外で必要なものはsendmailコマンドを備えた主要MTA(sendmail、Postfix、qmail、…)だけです。(nkfすら不要)


技術解説

ここからしばらくはどうやって送るかの技術解説ですが、解説すっ飛ばして出来上がったシェルスクリプトの使い方を教えろという方はこちらへどうぞ。


step(1/3) ― 英数文字メールを送ってみる

まずはsendmailコマンドだけで済む内容のメールを送り、sendmailコマンドの使い方を覚えましょう。

といっても、 -i-t オプションさえ使えばOK。他にもいろいろありますが、覚える必要ありません。

どーしても意味を知っておきたいという人は、こことかここのmanを読んできてください。

重要なのは、 一定の書式のテキスト作って、標準入出力経由で sendmail -i -t に送る。 それだけです。

まずは適当なテキストエディターで下記のテキストを作りましょう。


メールサンプル(mail1.txt)

From: <YOUR-ADDRESS@YOUR-DOMAIN.OCM>

To: <YOUR-ADDRESS@YOUR-DOMAIN.OCM>
Subject: Hello, e-mail!

Hi, can you see me?


メールテキストを作る時のお約束は、 ヘッダー部分のセクションと本文セクションの間に空行1つを挟むこと です。これを怠るとメール届きませんよ。

To:行にはアナタの本物のメアドを入れてください。(そーしないと届きません)

それからFrom:行にも何らかのメアドが必要ですが、あまりいい加減なものを入れるとspam判定されるかもしれないので、マジメに入れておいてください。

「Cc:とかBcc:も追記したら、CcもBcc:も送れるのか」 って、いい質問ですね。そのとおりです。

それができたら、いざ送信! 次のコマンド打つだけです。


メール送信

$ cat mail1.txt  |

> sendmail -i -t
$

コマンド連打すれば、連打した数だけ届きます。はい、届きましたねー?


step(2/3) ― 本文が日本語のメールを送ってみる

このドキュメントを読んでいるのは日常的に日本語を使う人々のはずですから、次は本文が日本語のメールを送ってみましょう。

ここまではとても簡単です。

まずは、本文日本語を交えたメールテキストを作ります。


メールサンプル(mail2.txt)

From: <YOUR-ADDRESS@YOUR-DOMAIN.OCM>

To: <YOUR-ADDRESS@YOUR-DOMAIN.OCM>
Subject: Hello, e-mail!
Content-Type: text/plain;charset="UTF-8"
Content-Transfer-Encoding: base64

やぁ、これ読める?


本文に日本語文字が入った他に、ヘッダーにContent-Type: text/plain;charset="UTF-8"Content-Transfer-Encoding: base64、 を付けました。これは、本文がUTF-8エンコードされていること、さらにそれがBase64エンコードされているということを知らせるためです。なぜBase64化するかというと、UTF-8は<0x80>以上の文字を含んでいてSMTPサーバーを通過できる保証がないからです。

では具体的にどうコマンドを打てばよいかというと、


メール送信

$ CR=$(printf '\r')

$ cat mail2.txt | while read -r line; do
> case "$line" in
> '') echo
> cat |
> sed '/[^'"$CR"']$/s/$/'"$CR"'/' |
> sed '/^$/s/$/'"$CR"'/' |
> base64
> ;;
> *) printf '%s\n' "$line"
> ;;
> esac
> done
$

です。あ、base64コマンドがPOSIX外じゃないかって。大丈夫です。こんなこともあろうかとPOSIXによる互換品を作っておきました。(→技術解説はこちら

というわけで、届きましたか?

このシェル文は、whileループでヘッダーセクション(最初の改行が現れるまで)をそのまま通し、本文セクションに到達したら、catコマンドによって以降の標準入力データをごっそり奪ってbase64コマンドによるエンコードをする、というものです。そうそう、メールに関する仕様によってbase64コマンドに渡す前に、改行コードをCR+LFにしておく必要があるのでご注意ください。(base64コマンドに渡さない場合は、後のsendmailコマンドが自動的にCR+LFにしてくれます)


step(3/3) ― 件名や宛先も日本語化したメールを送る

From:やTo:は許せるとしても、Subject:(件名)には日本語使いたいですよね。

しかし、メールヘッダーには生のJISコード文字列を置いてはいけません、ってか置いた場合の動作は保証されません。ヘッダーはメールに関する制御情報を置く場所なので、あまりヘンな文字を置いてはいけないのです。

なので、ヘッダー部分に日本語を置くにはBase64エンコードもしくは、quoted-stringエンコードすることになっています。ここではより一般的なBase64エンコードを使うやり方を紹介します。


メールで使えるBase64エンコード済JISコード文字列の作り方

例として、送りたいメールの件名は「ハロー、e-mail!」、そして宛先は「あなた」ということにしてみます。

冒頭で好きな文字列を書いたechoコマンドを入れて、base64とxargs、そしてprintfコマンドに流すだけです。


「ハロー、e-mail!」をエンコード

$ echo -n 'ハロー、e-mail!'             | # 改行コードが入らぬように-nオプションを付ける

> base64 |
> xargs printf '=?UTF-8?B?%s?=\n' # 生成文字列の両端をエンコード種別を記した文字列で挟む
=?UTF-8?B?44OP44Ot44O844CBZS1tYWlsIQ==?=
$


「あなた」をエンコード

$ echo -n 'あなた'                      |

> base64 |
> xargs printf '=?UTF-8?B?%s?=\n'
=?UTF-8?B?44GC44Gq44Gf?=
$

コード中のコメントにも書きましたが、ポイントは3つ。


  1. 最初に流す文字列には余計な改行をつけぬ事(echoの -n オプションを使うなどして)

  2. Base64エンコード

  3. 生成された文字列の左端に =?UTF-8?B? を、右端に ?= を付加

です。


さぁ送ってみよう

というわけで、上記の作業で生成された文字列をメールヘッダーにコピペすればよいのです。さあ、早速メールテキストを作ってみましょう。


メールサンプル(mail3.txt)

From: <YOUR-ADDRESS@YOUR-DOMAIN.OCM>

To: =?UTF-8?B?44GC44Gq44Gf? <YOUR-ADDRESS@YOUR-DOMAIN.OCM>
Subject: =?UTF-8?B?44OP44Ot44O844CBZS1tYWlsIQ==?=
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: base64

やぁ、これ読める?


これを先程のmail2.txtと全く同じように送ればいいわけです。今度は宛名も件名もきちんと日本語文字列で届いたことを確認してください。


この作業を自動で行うシェルスクリプト

ここまでのチュートリアルを見れば、やり方はわかっていちいち手作業で宛先や件名のエンコード文字列を貼る作業など面倒臭いですね。そこで、ヘッダー部分(From:、Cc:、Reply-To:、Subject:)も本文も日本語で書かれたメールを与えるだけで必要なエンコードをしながらsendmailコマンドで送信してくれるsendjpmailコマンドを作り、GitHubに公開しました。


つかいかた

例えば次のように、ヘッダーや本文に日本語の含まれたメールファイル(Content-Type:Content-Transfer-Encoding:ヘッダーも省略可)があった時、


メールサンプル(mail4.txt)

From: わたし <YOUR-ADDRESS@YOUR-DOMAIN.OCM>

To: あなた <YOUR-ADDRESS@YOUR-DOMAIN.OCM>
Subject: ハロー、e-mail!
Content-Type: text/plain;charset="UTF-8"
Content-Transfer-Encoding: base64

やぁやぁ、これも読める?


次のようにして(標準入力から渡すのも可)sendjpmailコマンドを一発叩けば、必要なBase64エンコーディングやContent-Type:ヘッダー等付加を行って正しいメールを送ってくれます。


sendjpmailコマンドによるメール送信(とても簡単!)

$ sendjpmail mail4.txt

$

繰り返しますが、追加で必要なソフトはsendmail、Postfix、qmail等の主要MTAだけです。nkfはもはや不要です。


(オマケの宣伝)

このTipsを始め、POSIX原理主義を徹底したレシピ集同人誌「Shell Scriptライトクックブック2014 第二版」をコミケットスペシャル6で頒布します。

日時と場所は 2015/3/28(土) 幕張メッセ K-58b ですんで、どうかひとつ。