sortコマンドで複数キーによるソート

sort コマンドの挙動がわかりにくかったのでメモです。

サンプルとして次のような3つのフィールドを持ったデータを使います。

sort_sample.tsv

5	aab	5
1	abb	5
10	aab	10
5	abb	1
1	baa	10
1	aba	5
10	bab	5
10	abb	10
1	bab	1
1	aab	5

sort コマンドではキーに関してソートを行います。

このキーはデフォルトで1行全体です。つまり

$ sort sort_sample.tsv
1	aab	5
1	aba	5
1	abb	5
1	baa	10
1	bab	1
10	aab	10
10	abb	10
10	bab	5
5	aab	5
5	abb	1

のように、得られる結果としては第1フィールドに関してASCIIコードの昇順にソートされ、第1フィールドの同じものは第2フィールドに関してASCIIコードの昇順にソートされ、第1フィールドも第2フィールドも同じものは第3フィールドに関してASCIIコードの昇順にソートされたものとなっています。

-k オプションを使ったソート

このソートをもっと柔軟に行うために用意されているのが -k オプションです。
次のように指定します。

-k POS1[,POS2]

ここで、POS1はキーの最初となる位置、POS2はキーの最後となる位置です。POS1以降全体がキーとなります。

POS1やPOS2は次のように指定します。

フィールドの位置[.文字の位置][ソートのオプション]

※POS1の文字の位置を省略すると1になり、POS2の文字の位置を省略するとそのフィールドの最後の文字までが対象になるみたいです
※第1フィールド以外は区切り文字を指定しない限り区切り文字が含まれるらしく、区切り文字を除いた2文字目を指定するには3にする必要があります

例えば、第2フィールドでソートして、第2フィールドが同じものは第3フィールドでソートしたければ

$ sort -k 2 sort_sample.tsv
10	aab	10
1	aab	5
5	aab	5
1	aba	5
5	abb	1
10	abb	10
1	abb	5
1	baa	10
1	bab	1
10	bab	5

となります。

これを第2フィールドでソートして、第2フィールドが同じものは第3フィールドで数値としてソートしたければ

$ sort -k 2,2 -k 3n sort_sample.tsv 
1	aab	5
5	aab	5
10	aab	10
1	aba	5
5	abb	1
1	abb	5
10	abb	10
1	baa	10
1	bab	1
10	bab	5

となります。-k 2,2 のように範囲を絞るのがポイントです。

これが -k 2 のままだと

$ sort -k 2 -k 3n sort_sample.tsv
10	aab	10
1	aab	5
5	aab	5
1	aba	5
5	abb	1
10	abb	10
1	abb	5
1	baa	10
1	bab	1
10	bab	5

のように所望の結果は得られません。これはおそらく第2フィールドでASCIIコードとしてソートし、第2フィールドが同じものは第3フィールドでASCIIコードとしてソートし、第2フィールドと第3フィールドが同じものは第3フィールドで数値としてソートする、という無意味な指定になっているんだと思います。

最後に・・・

そんなわけで、-k オプションを駆使すれば第1フィールドはASCIIコードとして昇順にソートして、第2フィールドは2文字目に関してASCIIコードとして降順にソートして、第3フィールドは数値として降順にソートするという変態的なことまでできるわけです。

$ sort -k 1,1 -k 2.3,2.3r -k 3,3n sort_sample.tsv
1	aba	5
1	abb	5
1	baa	10
1	aab	5
1	bab	1
10	abb	10
10	aab	10
10	bab	5
5	abb	1
5	aab	5

※区切り文字を指定した場合は -k 2.2,2.2r になります

sort コマンド便利ですねっ!!!!

参考