カテゴリー別アーカイブ: 自由ソフトウェア

gnu.org日本語翻訳ミーティング参加

先日、「www.gnu.orgの日本語訳について」のミーティングに参加してきました。最近のFSIJ月例会はMumbleを用いたリモート開催を増やしているので、リアルミーティングは久しぶりです。

当日の配布資料はhttp://git.savannah.gnu.org/cgit/www-ja.git/tree/doc/meeting-2017-01.txtにあります。そちらにもgnu.org翻訳作業への参加方法が書かれていますが、興味のある方はSavannahwww-jaプロジェクトのメーリングリスト(閲覧は要登録)や、gitリポジトリの参照(こちらはanonymous clone可能 git://git.savannah.gnu.org/www-ja.git)も見てみてください。

個人的な活動 – 機械翻訳

私はこの1年ろくな活動ができていなくて申し訳なかったのですが、以前から考えていた「www-jaをコーパスとして機械学習による翻訳モデルを構築する」という活動に手をつけ始めました。TensorFlowのseq2seqサンプルを若干修正してWMTの代わりに任意のテキストファイルを与えられるようにしたコードは以前作成した(https://github.com/knok/tf-seq2seq-mod)ので、あとはデータセットを作成するだけです。

まずは以下のようなPython scriptを作ってみました。Python2, polib, natto-pyで動きます。

# -*- coding: utf-8 -*-
#

import polib
import os, sys
import re

target_dir = "./www-ja"
pos = []

for root, dirs, files in os.walk(target_dir):
    for file in files:
        if file.endswith(".ja.po"):
            po = polib.pofile(os.path.join(root, file))
            pos.append(po)
#
#import pdb; pdb.set_trace()

pair = {}
for po in pos:
    for p in po:
        msgid = p.msgid
        msgstr = p.msgstr
        pair[msgid] = msgstr
#

from natto import MeCab
m = MeCab("-Owakati")

fname_in = "input.txt"
fname_out = "output.txt"

with open(fname_in, "w") as fi, open(fname_out, "w") as fo:
    for key in pair.keys():
        val = pair[key]
        p_key = m.parse(key.encode('utf-8'))
        p_text = m.parse(val.encode('utf-8'))
        if re.match(r"^[0x00-0x7f]*$", p_text):
            continue
        fi.write(p_key)
        fi.write("\n")
        fo.write(p_text)
        fo.write("\n")

以下のような感じで実行しています。

$ python translate.py --data_dir /path/to/text \
 --train_dir /path/to/save-data --size 400 \
 --en_vocab_size 30000 --fr_vocab_size 30000 \
 --num_layers 1 --batch_size 5
Creating 1 layers of 400 units. Created model with fresh parameters.
Reading training data (limit: 0).
global step 200 learning rate 0.5000 step-time 2.50 perplexity 3205.53
 trg = FSF セミナー : GPL と LGPL の 詳細 の 研究 と 分析
 hyp = 0000 0000 0000 > > > > > > > > > > > > > > > > > > > > > >
(中略)
global step 10000 learning rate 0.4950 step-time 2.37 perplexity 1.79
 trg = < a href = " / fun / jokes / quayle . html " > クエール 氏 </ a >
 hyp = < a href = " / fun / jokes / vi . html " > ネットワーク さ </ a >

残念ながら利用可能なGPUを持っていないので、CPU演算のみで処理しています。今のところDebianで深層学習フレームワークがtesting(stretch)入りしているのはCaffe, Theanoの2つだけで、TensorFlowや自分がよく使っているChainerは入っていません。まあこのあたりはupstreamの更新も頻繁なので、あえてパッケージを使わなくともvirtualenv+pipで適時入れるほうが良い気もします。

とはいえ、GPUを使って深層学習を行うということは、ほとんどの場合プロプライエタリなソフトウェアに依存することと等価なので、「gnu.orgの翻訳」ということに限ればむしろCPUのみでの処理の方が望ましいのかもしれません。

自由なコンピューティング

実際、ミーティング当日も自由なコンピューティング環境がより一層損なわれているという話がありました。gniibeさんはChromebookにLibrebootを苦労して入れているようです。

自分もここ数日いろいろとひどい目にあいました。Xbox 360のSmartGlassを使ってみようとしたのですが、Windows 8.1の場合Xbox 360で利用しているLiveアカウントとの関連付けをWindows自身のログインアカウントと紐付けないと利用できないのです。Windows 8以降、Microsoftは極力Liveアカウントでのログインをさせるよう誘導しているのですが、8.1(とおそらく8)でそれが強制されるというのは厳しく、利用を諦めました。ただ、Windows 10ではこの点は改善されているようで、SmartGlassを含むストアアプリはアプリ単位で個別に利用するLiveアカウントを設定したり、そもそもLiveアカウントの必要ないアプリは普通にローカルアカウントのまま利用できるようになっていました。なので、Windows 10マシンでSmartGlassを利用しています。

iPhone, Android端末なども実質iCloud, googleアカウントが必須に近い状態ですが、Windowsも同様の仕組みに倒しているわけで、自由からより通い方向に進んでいるなあと痛感しました。

 メンテナ不在問題

現在メンテナが不在なGNU Projectのソフトウェアについていくつか名前が挙げられました。

一つはGNU bisonです。これは割と致命的な気がしたのですが、GCCは4以降独自パーサ(gniibeさんによると、そもそもC++が自由文脈文法でないため)を採用しているとのことなので、案外問題は少ないのかもしれません。メジャーな言語処理系でもbison(yacc)必須なものがぱっと浮かびませんし。

もうひとつはGNUbikだそうです。うーん、これは重要でもないですかね。

Mini Debian Conference Japan 2016参加

2016/12/10に開催されたMini Debian Conference Japan 2016に参加してきました。場所は日本橋のサイボウズオフィス内で、LibreOffice Kaigi 2016と同時開催でした。自分も一応両方に参加するつもりで申し込みも両方にしたのですが、結果としてDebian側の話ばかり聞いていました。

その時の様子はハッシュタグ#debianjpでツイッターを検索するとある程度様子が伺えると思います。自分が参加しているセッションについてはある程度ツイートしています。せっかくなのでMomentsを使ってみようかと思ったのですが、全部のツイートが出てこない上に時系列に並んでいない、並び替えはすべて手動という使い勝手の悪いインターフェースで断念しました。

個人的には音響モデルを含めて自由ソフトウェア実装として作ったバーチャルシンガー徵音梅林(ちおんメイリン)に最もインパクトを受けました。発表は台湾の張正一(Chou Shouichi)さんでした。

発表資料の中でも触れられていましたが、クリプトン・フィーチャーの商用ソフトウェアであるVOCALOIDは作成できる内容に制限があります。初音ミクのEULA(PDF)にありますが、公序良俗に反するなどの歌詞を含めることを禁止しています。現実にはそういった内容のものが作成、公開されているのですが、規約レベルではアウトです。張さんは、ギターを燃やすパフォーマンスの写真などを交えて「音楽活動は自由であるべきだ」と主張されていました。これには強く同意します。

ソフトウェアのベースはWORLD(修正BSDライセンス)というパテントフリーなものを使っているとのことでした。音響モデルもマスコットキャラクターもCC-BY 3.0で配布しています。

残念ながら、まだフロントエンドについては実装がこなれていないようです。デモではWineでVOCALOIDのエディタを利用されていました。まだ日本で利用している例はほとんどなさそうですが、頑張って欲しいと思います。

自作辞書のブートストラップを考える

かな漢字変換や形態素解析といった処理を行うにあたって、辞書は必要不可欠な存在です。これを一から自作することを考えています。

  1. 適切な単語のリストの作成
  2. 単語に対する読み、品詞の追加
  3. 単語に対するコスト値の算出
  4. データソースはWikipediaを使う

こんな感じでおそらくはできるでしょう。時間はかかると思うので、ゆっくり考えていくつもりです。

今はまず教師なし機械学習による単語分割を使って、単語リストを作ることを試みています。これについては階層Pitman-Yor過程に基づく可変長n-gram言語モデル(VPYLM)の実装が複数あり、それを使うことである程度下処理ができます。

Wikipediaの記事はhttps://dumps.wikimedia.org/jawiki/からダウンロードできます。本文のダンプはjawiki-latest-pages-articles.xml.bz2というファイル名です。これをWikipedia ExtractorというPythonスクリプトでプレーンテキストに変換できます。変換時に警告が出ますが、それはたいてい自己参照に関するものなので、放っておいても大丈夫そうです。

さしあたって、展開ディレクトリ/AA/wiki_00を対象にlatticelmをデフォルトパラメータで実施したところ、以下のような感じになりました。


$ head -10 data/AA/wiki_00
< doc i d=" 1 "u rl ="h t tps:/ / ja. wik iped ia. o rg/wi ki?cur id= 1 " ti tl e=" W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月 ">
W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月
< u l >
< /d oc >
< doc i d=" 2 " url =" htt ps :// ja. wik iped ia. o rg/wi ki?cur id= 2 " ti tl e=" W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月 ">
W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月
B e l o w i s a l i s t o f t h e m o s t r e c e n t d e l e t io n s .
A l l t i m e s s h o w n a r eser v er ( U . S . P a ci fi c ) t i m e .
< /d oc >
< doc i d=" 5 " url =" htt ps :// ja. wik iped ia. o rg/wi ki ?c urid= 5 " ti tle =" ア ン パサ ンド ">

ちょっと分割されすぎですね…ハイパーパラメータはいろいろとあるので、チューニングはいろいろと必要そうではあります。他に試された人の解説によると、初期状態がすべて分割されている点が大きいとのことです。

以前の記事で紹介した「続・わかりやすいパターン認識 教師なし学習入門」には通常のPitman-Yor過程までは解説されており、VPYMLに関してはなんとなくでしか理解出来ていません。もう少し自分の理解も深めたいところです。

単語分割自体は系列ラベル問題とみなしてRecurrent Neural Networkベースで処理することも出来るそうですが、教師あり学習なのできちんとしたデータセットを作る必要があります。そこにたどり着くまでのところを、教師なし学習との組み合わせでなんとか実現したいところです。

GNU screen tips

普段使っているGNU screenのtips」という記事をQiitaに投稿しました。簡単にまとめると

  • multi attach (-xオプション)
  • fit コマンドによる画面のリサイズ
  • -Xオプションの応用
  • C-a ” (windowlist -b)カーソルキーでのウィンドウ選択
  • number コマンドでウィンドウ番号入れ替え
  • title, setenv

という感じです。

これ以外に、screen上のすべてのウィンドウでgpg-agentにパスワードをキャッシュさせるpinentry-cursesを特定のウィンドウで待機させる、ということをやっているのですが、使っているスクリプトがgniibeさん作なので書きませんでした。というかあのスクリプトはそこそこ問題なく動いている感じなので公開してもらえたらなあ、と思っています。

 

2016年4月FSIJ月例会 BBG-SWD

先日FSIJ月例会がありました。タイトルは「SWDプログラマの決定版 BBG-SWD」です。

一口でまとめると、ARMによるSWD(Serial Wire Debug)をBeagleBone Green上で使えるようにしたという話でした。

そもそもの背景には、gniibeさんが販売しているハードウェアFST-01FSM-55の製造過程があります。実際にハードウェアの製造を行うSeeed Studioでは、ファームウェアの書き込みにST-Link/V2というJTAGデバッガを、Windows上で動作するST Micro提供のソフトウェアで使っているという問題があります。ファームウェア書き込み部分も自由ソフトウェア、ハードウェアで置き換えることを目的としているそうです。これによって、”Respects Your Freedom Hardware product“の認証を取得する資格が得られるようです。ST Microのプロプライエタリなソフトウェアを介さないので、その部分でマルウェアを仕込まれるような可能性も低減できます。

ソフトウェアのリポジトリはhttp://git.gniibe.org/gitweb/?p=bbg-swd.gitにあります。この中には、BeagleBone Greenのbone-kernelと呼ばれる種類のLinux kernelに含まれるPRUSS(Programmable Realtime Unit SubSystem)のドライバ(uio_pruss)を介して動作するプログラムと、それをOpenOCDで利用可能にするためのOpenOCDへのパッチから構成されています。

gniibeさんはこの成果をhackster.ioのIoTコンテストに応募したそうですが、ファイナリストまでは選出されたものの受賞には至らなかったとのことでした。

Raspberry PIを筆頭としたワンチップマイコンは広く流行っていますが、こういった低レイヤーまで自由なコンピューティング環境を提供しているものはなかなか無いようです。

有志によるリバースエンジニアリングの成果でそれなりに自由なOrange PIシリーズは、熱に弱いという問題があるそうです。自分も購入したのにここ数ヶ月使っていなかったので知りませんでした…

今の時期暖かくなってきましたが、そんな環境でも動作するというgniibeさんのおすすめはWandboardだそうです。

自由なデータの重要性

先日、NEologd Casual Talksというイベントに参加してきました。mecab-ipadicに新しいエントリーを追加するmecab-ipadic-neologdに関するイベントでした(Togetterまとめ)。

その中で改めて感じたのは、自由なデータの重要性です。イベントでは「言語資源」という観点でのデータの重要性が語られていました。形態素解析器の辞書として、継続的に更新・リリースがなされているのはNEologd以外ほとんどないという指摘がありました。

自分も2012年にSoftware Freedom Dayの国内イベントで「自由なデータ」という題で発表をしました。

今になって改めてこの資料を見ると、現在は若干状況が変化している部分もあります。NEologdの出現は、まさしくその一つです。

また、日本語の文章チェッカーとしてtextlintという実装の日本語ルールを作成・公開している人がいます。これまでLanguageToolに若干のルールをコミットしてきましたが、textlintのほうがかなり充実している感じです。

最近は機械学習をやってみたりしているのですが、これもまさにデータの質がかなり重要な領域です。変なデータを元に学習すると、そもそも学習が収束しなかったり、期待しない結果を返すモデルができてしまったりします。この領域でも、チュートリアルに使われる定番のデータセットが公開されています(MNIST, CIFAR-10等)。

最近、自分も画像分類タスクのテストのために作ったデータセットはあるので、なんとか公開できる形にできないかと考えています。実データはInstagramにあるもので、個々の画像のライセンスを確認しないまま集めて分類したのですが、画像URLの一覧という形なら問題なく配布できると思います。オライリー「実践 機械学習システム」でも感情分析用のツイートデータを人力で分類したもののID一覧から実データを取得する、という方法が取られていました。

 

実際に自分でデータの分類をやってみると、「どこまでをこの分類に含めるべきか」という根源的な問題を実感できます。実際、単純に検索しただけだと「明らかにおかしい」というものから「これはどうなんだろう」というものも出てきます。

この点について、先日の発表の中で印象的だったのが片山さんの「ファッションが大好きなので無限に洋服の文字列データを眺めるのが楽しかった」という部分でした。特定のドメインのテキストでも、きっと同じような感覚があるのだろうとなんとなく感じています。

ともあれ、公開できる形になったら、改めて告知したいと思います。

Orange PI PC

g新部さんが強くプッシュしているOrange PI PCを購入しました。実際に購入したのは先月ぐらいだったのですが、最近ようやくまともに使いだしたところです。

Orange PIシリーズは複数あるRaspberry PIのフォロワーの一つです。私は以前秋月電子で同じような”Banana PI”というのを見かけたことがあるのですが、気が付くと手に入らなくなっていました。

g新部さんが最も良しとしている点は、ハードウェアメーカー(Allwinner)が情報開示に積極的にであることだそうです。Raspberry PIはその点で確かに厳しいようです。

そのあたりの話が11月のFSIJ月例会でありました。その時の発表内容をTogetterでまとめたので、興味のある方はご覧ください。月例会のページにもいくつか情報があります。

私の興味はH.264のハードウェアエンコーディングにあります。g新部さんからお勧めされた時に、Allwinner H3のスペック的にハードウェアエンコーダーを持っていることを確認して購入しました。しかしまだそこを動かすには至っていません。情報はそこそこあるようなので、なんとかしたいところです。

LanguageToolで長音記号チェック

漢字変換ミスで、たまに長音記号がかな文字以外の後に置かれる事例を見かけたので、それをLanguageToolのルールにして取り込んでもらいました

現状ある日本語のルールをざっと見たのですが、うまい具合に表現する方法が思い浮かばなかったので、思い切ってメーリングリストで聞いてみました。

メイン開発者のDanielさんは親切で反応も早い方で、「正規表現でUnicodeの範囲を使えばできる」という方法を示してくれました。また、過去にも日本語のルールを書いているSilvanさんからは、JavaにおけるUnicodeのクラス表現(\p{IsXxx})を紹介してくれました。

これらを踏まえ、以下のようなルールをpull requestとして書き、無事masterにマージしてもらえました。

                <rule id="SINGLE-MARKON" name="長音">
                  <pattern case_sensitive="no">
                    <token regexp="yes">[^\p{IsKatakana}\p{IsHiragana}]+</token>
                    <token >ー</token>
                  </pattern>
                  <message>不適切な長音符</message>
                  <example><marker>リンカー</marker></example>
                  <example correction=""><marker>隣家ー</marker></example>
                </rule>

カタカナとひらがな以外が長音記号の前にあるとき、LanguageToolは警告を出します。

$ echo "隣家ー" |java -jar languagetool-commandline.jar -l ja-JP -c UTF-8
Expected text language: Japanese (no spell checking active, specify a language variant like 'en-GB' if available)
Working on STDIN...
1.) Line 1, column 1, Rule ID: SINGLE-MARKON[1]
Message: 不適切な長音符
隣家ー
^^^
Time: 529ms for 1 sentences (1.9 sentences/sec)

この機会に、Doc-ja Wikiの”LanguageTool使い方メモ“も若干修正しました。自前のgrammar.xmlを指定して起動する方法と、ソースコード上で変更をしたときにルールのテストをする方法について新た説明を加えています。

LanguageToolのリリースバージョンは現在2.9ですが、次期バージョン3.0ではgrammar.xmlの書式も変わっているため、いずれWiki内の説明もそちらに合わせて修正したいところです。

ownCloudのDBマイグレーション失敗とリカバー

ふとしたことから、手元のsid環境を更新したのですが、その時にownCloudが5.0.25から7.0.2へと大きく変わってしまいました。ownCloudはアップブレード機能を持っている(web, occコマンド)のですが、それが途中でこけてしまったので、対処方法をblogに残しておきます。

5.0.25から7.0.2へ移行

自分はsid環境を、lxcコンテナの一つに持っています。その中でownCloudも運用しています。最初に導入したときのバージョンは5.0.25だったのですが、この記事を書いている辞典では7.0.2が最新です。5->6のメジャーアップグレードをはさめばうまくいったのかもしれませんが、自分のケースではデータベースの移行がうまくゆきませんでした。バックエンドにはSQLite3を使っています。

oc_lucene_statusでのエラー

アップグレード時に発生したエラーログの一部を以下に示します。

{"app":"core","message":"Simulated database structure update failed (exception OC\\DB\\MigrationException' with message 'An exception occurred while executing
'INSERT INTO oc_lucene_status (fileid, status) SELECT fileid, status FROM __temp;
__oc_lucene_status':\n\nSQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: oc_lucene_status.fileid'

oc_lucene_statusというテーブルの移行に失敗しています。このテーブルはLuceneによる全文検索用のものと思われますが、自分自身が全文検索を使ったことがこれまでなかったので、単純にテーブルの削除と再作成をすればよいだろうと推測し、まずはバックアップを取ったうえでdropしてみました。

単純にdropをしただけでは、当然テーブルがなくなるだけなので、やはりアップグレードに失敗します。その時のエラーは以下の通りです。

$ sudo occ upgrade
Turned on maintenance mode
Checked database schema update
Updated database
An exception occurred while executing 'DELETE FROM "oc_lucene_status" WHERE "fileid" IN (
                                        SELECT "fileid"
                                        FROM "oc_lucene_status"
                                        GROUP BY "fileid"
                                        HAVING count("status")  1
                                )
                      ':
SQLSTATE[HY000]: General error: 1 no such table: oc_lucene_status
Turned off maintenance mode
Update successful

データベースのテーブルの定義はXMLで書かれています。ソースコードのapps/search_lucene/appinfo/database.xmlが該当するファイルです。これを見て適当に見よう見まねでテーブルを作ってみたところ、うまくゆきませんでした。

$ sudo occ upgrade
Turned on maintenance mode
Unknown database type integerv requested, Doctrine\DBAL\Platforms\SqlitePlatform may not support it.
Turned off maintenance mode
Update successful

これはまずいと思い、バックアップしておいた方のデータベース上でのテーブル構造を参照してみました。

sqlite> .schema oc_lucene_status
CREATE TABLE "oc_lucene_status" ("fileid" INTEGER DEFAULT 0 NOT NULL, "status" VARCHAR(1))
CREATE INDEX status_index ON "oc_lucene_status" (status ASC)

この結果を参考に、新しい方のデータベースで同じテーブルを作成し(表示されているSQLをそのまま実行する)、再度アップグレードを試みたところ成功しました。

$ sudo occ upgrade
Turned on maintenance mode
Checked database schema update
Updated database
Turned off maintenance mode
Update successful

無事アップグレードに成功し、今では普通に利用できています。

caffから受け取った署名を自分の鍵へ取り込む(gpg import)

ずいぶん間が開いてしまいましたが、GnuPG key signの作業をする(signing-party/caff)の続きです。前回は自分が相手に対して署名を行い、その結果を送るための手続きについて説明しました。今回はその逆、自分が受け取った相手からの署名を自分の鍵に追加する方法について説明します。

メールをファイルに保存

相手がcaffを使っている場合、pgp鍵のおそらくは各IDごとに1通のメールが届きます。

pub   4096R/9C0C1404 2009-09-11
                 指紋 = 9945 2B4D D28B EEAC 8AB5  C931 B066 62EC 9C0C 1404
uid                  NOKUBI Takatsugu <knok@daionet.gr.jp>
uid                  NOKUBI Takatsugu <knok@debian.org>
uid                  NOKUBI Takatsugu <knok@namazu.org>
uid                  NOKUBI Takatsugu <knok@fsij.org>
sub   4096R/07098680 2009-09-11
sub   2048R/861243E1 2012-08-23

私の鍵の場合だと、IDとしているメールアドレスは4つあるので、一人当たり4通メールが送られてきます。それらを1つづつファイルとして保存します。

メールの内容は、それぞれのIDに対応する、自身の公開鍵で暗号化されたpgp asciiメッセージです。

Subject: Your signed PGP key 0xB06662EC9C0C1404
Content-Transfer-Encoding: 7bit
Content-Type: multipart/encrypted;
 protocol="application/pgp-encrypted";
 boundary="----------=_1409005317-7907-1"
User-Agent: caff 0.0.0.638 - http://pgp-tools.alioth.debian.org/
To: knok@daionet.gr.jp
From: "Sender" <user@example.jp>
X-Mailer: MIME-tools 5.505 (Entity 5.505)
MIME-Version: 1.0
Message-Id: <20140825222158.8DAC3399@example.jp>
Date: Tue, 26 Aug 2014 07:21:58 +0900 (JST)

This is a multi-part message in MIME format...

------------=_1409005317-7907-1
Content-Type: application/pgp-encrypted; name="signedkey.msg"
Content-Disposition: attachment; filename="signedkey.msg"
Content-Transfer-Encoding: 7bit

Version: 1

------------=_1409005317-7907-1
Content-Type: application/octet-stream; name="msg.asc"
Content-Disposition: inline; filename="msg.asc"
Content-Transfer-Encoding: 7bit

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

hQIMA8hTZXMHCYaAAQ//XfTpsTvsdKaLNZ2TkYf/lDRtHo9DRjtSiFSYT4lWcFwF
9yxFzX8TL/L+7o2xZTZ7nXTOHAIMkgxi5KXnIDY5O8iNvrN9pqh3VmKDhB2GWHRD
yJGIBiQZ+bPavFWgiscWhy7FqhWfGi7XHBEbEIhfn9FBrziwgvLwkkC24ufUtnOM
/u39RDXrJKpi0GT0rMW+Ykg9vs6QNSdyDeBQRRS16KTRyljSGmRX/Q3y57WVw09v
(以下略)

メールの復号化

このメールをpgpコマンドで復号化します。

$ gpg -o msg mail.eml

次のユーザーの秘密鍵のロックを解除するには
パスフレーズがいります:“NOKUBI Takatsugu <knok@daionet.gr.jp>”
4096ビットRSA鍵, ID 07098680作成日付は2009-09-11 (主鍵ID 9C0C1404)
gpg: このセッションでgpg-agentは無効です
パスフレーズを入力:

gpg: 4096-ビットRSA鍵, ID xxxxxxx, 日付2009-06-19に暗号化されました
      “Sender <sender@example.jp>”
gpg: 4096-ビットRSA鍵, ID 07098680, 日付2009-09-11に暗号化されました
      “NOKUBI Takatsugu <knok@daionet.gr.jp>”

上記コマンド例では、入力されたファイルをmsgというファイル名で復号化しています。復号化した内容は、caffの標準テンプレートであれば以下のようになります。

MIME-Version: 1.0
X-Mailer: MIME-tools 5.505 (Entity 5.505)
Content-Type: multipart/mixed; boundary="----------=_1409005317-7907-0"

This is a multi-part message in MIME format...

------------=_1409005317-7907-0
Content-Type: text/plain; charset="utf-8"
Content-Disposition: inline
Content-Transfer-Encoding: binary

Hi,

please find attached the user id
        NOKUBI Takatsugu <knok@daionet.gr.jp>
of your key B06662EC9C0C1404 signed by me.

If you have multiple user ids, I sent the signature for each user id
separately to that user id's associated email address. You can import
the signatures by running each through `gpg --import`.

Note that I did not upload your key to any keyservers. If you want this
new signature to be available to others, please upload it yourself.
Note that I did not upload your key to any keyservers. If you want this
new signature to be available to others, please upload it yourself.
With GnuPG this can be done using
        gpg --keyserver subkeys.pgp.net --send-key B06662EC9C0C1404

If you have any questions, don't hesitate to ask.

Regards,
Sender

------------=_1409005317-7907-0
Content-Type: application/pgp-keys;
 name="0xB06662EC9C0C1404.1.signed-by-0x2E8162547E37CE41.asc"
Content-Disposition: attachment;
 filename="0xB06662EC9C0C1404.1.signed-by-0x2E8162547E37CE41.asc"
Content-Transfer-Encoding: 7bit
Content-Description: PGP Key 0xB06662EC9C0C1404, uid NOKUBI Takatsugu
 <knok@daionet.gr.jp> (1), signed by 0x2E8162547E37CE41

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQINBEqq3iEBEAC2u1/t+pm5FSK//w9Qp/KxHW17rM+tOxqBNqVAZbFpOFu9Sa1i
U/C85ujEHKRIuLyESfzLg3gQmeojTritYPzG04s+bP+bbjujMkZZGhbrl4VjWA0L
DLO7uxwS/4scnBrlOONPaeRfP43T8a1hK2E0+8uIGhJIUt5fyW0o7Cs1MvL6T7UZ
YEwUW9lqciFhYEXr2zC6JYuofLytG1nUUFY4gcLWX/XSi1NUutL7Z5uJK3Ct8nlc
2AxFmPWkTYOcU6HpePQLlB7YGdldS8+41QkeDJm+w+ljqfI1vrVFT+SIHza8RiuW
(以下略)

署名の取り込み

この復号化されたファイルには、添付ファイルとして鍵への署名があります。本文中にも説明がありますが、gpg –importコマンドを実行することで署名を自分の鍵に取り込むことができます。

$  gpg --import msg
gpg: 鍵9C0C1404:“NOKUBI Takatsugu <knok@daionet.gr.jp>”新しい署名を1個
gpg:     処理数の合計: 1
gpg:       新しい署名: 1
gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、classic信用モデル
gpg: 深さ: 0  有効性:   3  署名: 127  信用: 0-, 0q, 0n, 0m, 0f, 3u
gpg: 深さ: 1  有効性: 127  署名:  33  信用: 126-, 0q, 0n, 0m, 1f, 0u
gpg: 次回の信用データベース検査は、2016-07-04です

これを、届いたメールの数だけ実施します。さすがに毎回passphreaseを入力するのはしんどいので、gpg-agent等を使うと楽になります。gpg-agentはバージョン2系からついてきます。1.4系にはありません。エージェントの使い方はssh-agentとよく似ているので、そちらに慣れている人であれば楽に使えるでしょう。

私は以下のようなワンライナーで一括処理をするようにしています。

for i in *.eml; do gpg --yes -o msg $i ; gpg --import msg; done

公開キーサーバー上の鍵の更新

最後に、公開キーサーバーへ新しい(電子署名の増えた)鍵を送信して更新します。これは普段から公開キーサーバーに鍵を登録している人だけがすればよい作業なので、運用によっては不要な作業です。

$ gpg --keyserver pgp.nic.ad.jp --send-keys 9C0C1404
gpg: 鍵9C0C1404をhkpサーバーpgp.nic.ad.jpへ送信
$ gpg --keyserver keyring.debian.org --send-keys 9C0C1404
gpg: 鍵9C0C1404をhkpサーバーkeyring.debian.orgへ送信

以上で作業は終わりです。