cut, sort, uniq で生産性を5%向上させる
エンジニア、特にウェブ系のお仕事をしていると、いかに windows や mac が好きでも linux や BSD(mac も今や BSD ですが、、)で作業する時間が圧倒的に長くなりますよね。しかも大抵の場合 X もない CUI な環境で延々何時間も作業するわけです。
で、僕だけかもなのですが、その時間のほとんどは「テキスト処理」にあてられるわけです。TSV ファイルを特定の項目で並べ替えたり、ログファイルから何かの項目を抜き出して(UA とか)、それを種類ごとにカウントしてソートして出したり。
そんな訳で UNIX環境で perl や sed、awk 等の文字列処理が得意なスクリプト言語を良く使うようになりますよね。ちょっとしたものであれば 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を読んで下さい!