ページ

2017年11月20日月曜日

sedでメールヘッダーの継続行を一行にまとめる

1メール1ファイルの状態で、To:Cc:に特定のメールアドレスを含むものだけ抽出したいとか、From:行だけ全部取り出したいとかやろうとすると、ヘッダーの継続行の扱いがけっこう面倒くさいですね。
普通ならPerlやPythonで書けばよいのだろうけれど、sed で継続行を一行にまとめる処理を書いてみました。
sedは読み込んだ行はパターンスペースに配置して、このパターンスペースが操作の対象となりますが、もうひとつホールドスペースというのを持っていて、これをうまく使うことで複数行を処理できます。

サンプルコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
: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 行目
    sed /^$/qヘッダーにしか用はないので、ヘッダー部分が終了したら処理を打ち切ります。
  • 3 行目 〜 17 行目
    /^[A-Za-z]/{ ... }
    ヘッダー行の先頭部分だったらこのブロックの中を実行します。
  • 4 行目 〜 7 行目/^[Ff][Rr][Oo][Mm]:/b concat/^[Tt][Oo]:/b concat /^[Cc][Cc]:/b concat b nextFrom:, To:, Cc: のとき、:concat以降の結合処理にジャンプ(b concat)します。 そうでなければ、:nextのところにジャンプ(b next)します。:next以降にコマンドはないので、sed は次の行の処理に移ることになります。
  • 8 行目 〜 9 行目:concath; 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 のみです。

0 件のコメント:

コメントを投稿