はじめに
1メール1ファイルの状態で、To:やCc:に特定のメールアドレスを含むものだけ抽出したいとか、ときかくFrom:行だけ全部取り出したいとかやろうとすると、ヘッダーの継続行の扱いがけっこう面倒くさいですね。
普通ならPerlやPythonで書けばよいのだろうけれど、sed で継続行を一行にまとめる処理を書いてみました。
sedは読み込んだ行はパターンスペースに配置して、このパターンスペースが操作の対象となりますが、もうひとつホールドスペースというのを持っていて、これをうまく使うことで複数行を処理できます。
サンプルコード
:top
/^$/q
/^[A-Za-z]/{
/^[Ff][Rr][Oo][Mm]:/b concat
/^[Tt][Oo]:/b concat
/^[Cc][Cc]:/b concat
b next
:concat
h; n
:loop
/^[[:space:]]/{
H; x; s/\n[[:space:]]*/ /; x; n;
/^[[:space:]]/b loop
}
x; p; x;
/^[A-Z]/b top
}
:next
解説
1 行目
:top
:
で始まるのはラベル行です。
2 行目
/^$/q
ヘッダーにしか用はないので、ヘッダー部分が終了したら処理を打ち切ります。
3 行目 〜 17 行目
/^[A-Za-z]/{
... }
ヘッダー行の先頭部分だったらこのブロックの中を実行します。
4 行目 〜 7 行目
/^[Ff][Rr][Oo][Mm]:/b concat
/^[Tt][Oo]:/b concat
/^[Cc][Cc]:/b concat
b next
From:
, To:
, Cc:
のとき、:concat
以降の結合処理にジャンプ(b concat
)します。
そうでなければ、:next
のところにジャンプ(b next
)します。
:next
以降にコマンドはないので、sed は次の行の処理に移ることになります。
8 行目 〜 9 行目
:concat
h; n
現在の行をホールドスペースにコピーして(h
)次の行を読み込みます(n
)。
10 行目 〜 14 行目
:loop
/^[[:space:]]/{
H; x; s/\n[[:space:]]*/ /; x; n;
/^[[:space:]]/b loop
}
継続行を1行にまとめる処理です。
読み込んだ行が継続行であれば /^[[:space:]]/{
... }
のブロックの中が実行されます。
パターンスペースをホールドスペースに追加して(H
)、改行と継続行先頭の空白を単一のスペースに置換します(s/\n[[:space:]]*/ /
)。
x
はパターンスペースとホールドスペースを入れ替えるコマンドです。sedの操作はパターンスペースに対してのみ実行可能なため、このように入れ替えを行います。
次の行を読み込んで(n
)、さらに継続行であれば:loop
のところにジャンプしてループを続けます(/^[[:space:]]/b loop
)。
15 行目
x; p; x;
ホールドスペースには、1行にまとめられたヘッダー行が保存されているので、これを出力します。
16 行目
/^[A-Z]/b top
パターンスペースには次の行を読み込んであるため、ヘッダー行であれば先頭に戻って処理を続けます(/^[A-Z]/b top
)。
注意
[[:space:]]
[:space:]
はPOSIX正規表現で空白を示すメタ文字列です。文字セット([]
)の中でのみ効果を持つため、[[:space:]]
のように書きます。
POSIX正規表現を理解しない、伝統的なsedでは [
スペース
タブ
]
といったように書きましょう。ただし、ターミナルに表示されているこの行を安易にコピー&ペーストすると、タブがスペースに展開されていたりしますので注意が必要です。
b
コマンド
ラベルの後に ;
で区切ってさらにコマンドを書くことができるのは GNU sed のみです。