bonar note

京都のエンジニア bonar の技術的なことや技術的でない日常のブログです。

cut, sort, uniq で生産性を5%向上させる

エンジニア、特にウェブ系のお仕事をしていると、いかに windows や mac が好きでも linuxBSD(mac も今や BSD ですが、、)で作業する時間が圧倒的に長くなりますよね。しかも大抵の場合 X もない CUI な環境で延々何時間も作業するわけです。

で、僕だけかもなのですが、その時間のほとんどは「テキスト処理」にあてられるわけです。TSV ファイルを特定の項目で並べ替えたり、ログファイルから何かの項目を抜き出して(UA とか)、それを種類ごとにカウントしてソートして出したり。

そんな訳で UNIX環境で perlsedawk 等の文字列処理が得意なスクリプト言語を良く使うようになりますよね。ちょっとしたものであれば perl で短いスクリプトを書いてしまえばOKなのですが、あらかじめ入っているコマンドを使うだけでも相当の事ができるなあと最近思ったのでメモです。とはいっても UNIX geek な方にはあくびが出る程退屈な内容なのであまり期待しないで下さい。orz

思うに以下の3つのコマンドを使いこなせるだけで生産性が5%くらいは違います。

前準備

とりあえず弄る用のデータファイルを用意します。何か適当なDBとテーブルがあると仮定してそこからファイルにデータをdumpします。普通にやると Perl の場合であれば DBI, DBD::* を使用してDBに接続してSELECT分のステートメントハンドルを prepare して execute してfetchrow_hashref() みたいな感じだと思うのですが、本当に一度きりのちょっとした処理であればecho と mysql コマンドだけでOKです。mysql前提ですいません。

これ意外と知られていなかったりするのですが(そんなことないかな)、mysql コマンドは標準入力で与えられたSQLを実行して、その結果をtsvファイルとして出力します。

echo "SELECT id, name, height FROM friend" | mysql -u bonar mydb > output.tsv

出力を押さえたmysqldumpを行うより断然らくちんです。ただしindexが効いてることが確実にわかっているSElECT以外は危険かもなので注意です。

以下のようなファイルが出力されます。

bonar$ cat output.tsv
id   name   height
1    foo     181
2    bar     160
3    buzz    181
4    hoge    155
5    bonar   172

cut

本題に戻って。

cut は特定の文字で区切られた項目を分割します。重要なオプションは2つでデリミタ指定の-dと表示項目指定の-fです。

-d 区切り文字を指定する(デフォルトはタブ)
-f 表示する項目を指定。区切った順に左から1,2,3...

複数の項目を出す場合には -f にカンマ区切りで項目番号を指定します。こんな感じです。

bonar$ cut -f 1 output.tsv 
id
1
2
3
4
5
bonar$ cut -f 1,3,2 output.tsv 
id      name    height
1       foo     181
2       bar     160
3       buzz    181
4       hoge    155
5       bonar   172

csvファイルなら -d "," でデリミタをカンマにすればOK。

sort

ソートは文字通り、引数で指定されたファイルや標準入力から来た入力を並べ替えます。重要なオプションは3つで、

-k ソートに使用する項目を指定(区切り文字はタブ)
-r 結果を降順にする(逆にする)
-n アスキーソートではなく、数値ソートする。つまり、10, 101, 2 は 2, 10, 101 にソートされる

例えばこんな感じです。

身長順にソート

bonar$ sort -n -r -k 3 output.tsv 
3       buzz    181
1       foo     181
5       bonar   172
2       bar     160
4       hoge    155
id      name    height

名前のアスキー順でソート

bonar$ cut -f 2 output.tsv | sort
bar
bonar
buzz
foo
hoge
name

今いるディレクトリでサイズの大きいファイル上位3つとかもこんな感じで簡単です。

bonar$ ls -l | sort -r -k 5 | head -n 3
rw-r--r--     1 bonar  bonar  4884792 Nov  8  2006 vim7-daily-w32j.exe
rw-r--r--     1 bonar  bonar    22438 May  7  2006 Makefile
rw-r--r--     1 bonar  bonar    15792 Apr 12  2006 Filelist

uniq

uniqはソートされた入力から重複した行を取り除いたり、重複行を抽出したりするの便利。

-d ユニークな行のみを表示(デフォルト)
-u 重複している行のみを表示
-c それぞれ登場した回数をカウントして出力する

例えば先ほどのファイルから身長を抜き出して、重複した行を出すには以下のような感じです。

bonar$ cut -f 3 output.tsv | sort | uniq -d 
181

181だけが重複していることがわかります。あと出現回数を数える機能が何気に便利で、例えば /etc/passwd で設定されているログインシェルでどれが一番多いかを調べるのも一発です。

bonar$ cat /etc/passwd | cut -d : -f 7 | grep -v "#" | sort | uniq -c
   1 
   1 /bin/sh
   2 /bin/tcsh
  20 /usr/bin/false
   1 /usr/sbin/uucico

grep -v はコメント行を取り除くために入れています。あーでもこれいい例じゃないかも。。

あらかじめソートされている必要がある点に注意ですね。ソートされてないと全部読み込んでからじゃないと重複チェックできないのでしょうがないですね。。



とまあ、基本的なことを偉そうに書いてすいません。。uniqとか便利な割に意外と使われて無いかなと思って書いてみました。cat, head, tail, cut, sort, uniq で大概の集計処理とかデータファイル作成は出来るもんだなと思って勢いで。

詳しくはmanを読んで下さい!