Understanding how sed works(1)

テキストデータを加工するとき、sedは欠くことの出来ないツールです。しかし、sedが改行を読み込まないことはあまり知られていません。例えば、入力に含まれる改行を全て削除するために以下のようなスクリプトを記述したとしましょう。

$ sed -n -e 's/\n//g; p' <<EOF
SED IS
A STREAM
EDITOR.
EOF

期待に反し、このスクリプトは改行を削除しません。

SED IS
A STREAM
EDITOR.

sedは改行を含む一行を読み込んでパターンスペースと呼ばれるバッファにその内容を配置しますが、そのとき改行を一度捨ててしまいます。そのため、改行がバッファに読み込まれることはありません。sedがバッファの内容を出力するとき再び改行を追加してしまうこともあり、なかなか気付き辛いのです。


さて、sedはパターンスペースに加えもう一つのバッファを持っていることもあまり知られていません。二つ目のバッファはホールドスペースと呼ばれます。パターンスペースとホールドスペースは相互に複製、追加および交換をすることが可能です。

h
パターンスペースの内容をホールドスペースに複製する
H
ホールドスペースに改行を追加し、さらにパターンスペースの内容を追加する
g
ホールドスペースの内容をパターンスペースに複製する
G
パターンスパースに改行を追加し、さらにホールドスペースの内容を追加する
x
ホールドスペースとパターンスペースの内容を交換する

これらのコマンドはなかなか覚えられるものではありませんし、無理に覚える必要もありません。何より重要なのは、2つのバッファが操作出来る事実です。


sedは改行を読み込まないこと。sedはパターンスペースとホールドスペースを持っており、相互に操作が可能なこと。この2つのポイントを押さえることで、sedはより強力なツールに変貌します。では、パターンスペースとホールドスペースを活用して改行を削除するスクリプトを記述してみましょう。

H
$ {
  g
  s/\n//g
  p
}

難解な印象を受けるかもしれませんが、sedが動作する様を眺めれば理解は容易です(高解像度版は、こちら)。

実際にスクリプト実行してみると、改行が削除されることが分かります。

$ sed -n -e '
H
$ {
  g
  s/\n//g
  p
}
' << EOF
SED IS
A STREAM
EDITOR.
EOF
SED IS A STREAM EDITOR.

パターンスペースとホールドスペースを活用することでsedの可能性は一気に広がります。例えば、行指向で入力を反転する場合は以下のように記述します。

1! G
$p
h

こちらもsedが動作する様を眺めてみましょう(高解像度版はこちら)。

確かに行を反転していることが分かります。

まとめ

  • sedは行を読み込むときに改行を捨て、出力するときに改行を追加する
  • sedはパターンスペースとホールドスペースの2つのバッファを持っており、互いに操作することが出来る

GNU sedのマニュマルは大変よくまとまったドキュメントです。特に3.1 How sed Worksは必読です。比較的短い章ですが、sedの改行に対する振る舞いを完全に把握することが出来ます。

sedの動作を解説するにあたり入力からの改行の削除を題材にしましたが、trコマンドでほぼ同等の処理を行うことが出来ます。

> tr -d '\n' <<EOF
SED IS
A STREAM
EDITOR.
EOF
SED IS A STREAM EDITOR.

sedを用いると出力の最後に必ず改行が追加されます。注意してください。

参考

このエントリをまとめるにあたり、以下のページを参考にしました。