単語の出現頻度を調べる シェルスクリプトメモ(3)

単語の出現頻度を調べるには以下のようにコマンドどうしをパイプで連携させる方法がある。

$ cat somefile | tr ' ' '\n' | sort | uniq -c | sort -nr | head


このワンライナーで自分がよく理解できなかったのが、tr コマンドの後に使われている sort コマンド。tr の後に uniq -c を直接つなげちゃえば単語の出現頻度は数えられるじゃんと思っていた。だけど、実際やってみると重複している行が出てきてしまってうまくいかない。
そこで、

$ info uniq


で調べてみると以下の記述を発見。

   The input need not be sorted, but repeated input lines are detected
only if they are adjacent.  If you want to discard non-adjacent
duplicate lines, perhaps you want to use `sort -u'.  *Note sort
invocation::.


要は、uniq では連続した重複行はひとつにまとめるけど、連続してない場合はたとえ重複行がいくつあろうとも別々の行として扱ってしまうらしい。これで、tr と uniq の間に sort が必要な理由が分かった。


余談だけど、「The input need not be sorted, but repeated input lines are detected only if they are adjacent.」という説明は少しややこしいなと思った。「入力はソートされてる必要ない」って言ってるけど、このプログラムの動作に支障が無いだけで、重複行をひとつにまとめるという期待している動作には支障が出てしまう可能性がある。その辺も詳しく書いといてくれればもっと分かりやすいんだけどな。まあ、uniq がどんな風に動作するかを知ってれば info 読むまでも無く、uniq の前に sort を挟むのは当たり前なんだろうけど…。

参考

shell のちょっとしたテクニック - odz buffer:情報源
2007-01-31 - 技術メモ帳:単語の抽出方法(直接今回の話題とは関係ないが、参考になったのでメモ)