sortコマンドで複数キーによるソート
sort コマンドの挙動がわかりにくかったのでメモです。
サンプルとして次のような 3 つのフィールドを持ったデータを使います。
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 コマンドは指定されたキーを基準に、現在の locale の辞書式順序でソートを行います。このキーはデフォルトで 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 フィールドに関して辞書式順序の昇順にソートされ、第 1 フィールドの同じものは第 2 フィールドに関して辞書式順序の昇順にソートされ、第 1 フィールドも第 2 フィールドも同じものは第 3 フィールドに関して辞書式順序の昇順にソートされたものとなっていることがわかります。
-k オプションを使ったソート
このソートをもっと柔軟に行うために用意されているのが -k オプションで、次のように指定します。
-k POS1[,POS2]
ここで、POS1 はキーの最初となる位置、POS2 はキーの最後となる位置です。POS2 を指定しないと POS1 以降全体がキーとなります。
POS1 や POS2 は次のように指定します。
フィールドの位置[.文字の位置][ソートのオプション]
フィールドの位置も文字の位置も 1-origin です。POS1 の文字の位置を省略すると実質的に 1 を指定するのと同じになり、POS2 の文字の位置を省略するとそのフィールドの最後の文字までが対象になるみたいです。
例えば、第 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 フィールドを辞書式順序でソートし、第 2 フィールドが同じものは第 3 フィールドを辞書式順序ででソートし、第 2 フィールドと第 3 フィールドが同じものは第 3 フィールドを数値順でソートする、という無意味な指定になっているんだと思います。
文字の位置についての注意点
前述のとおり、文字の位置は 1-origin なんですが、第 1 フィールド以外については注意が必要です。
というのも、区切り文字を -t
オプションで指定するか、-b
オプションを指定するかしない限り、whitespace が 1 文字目とみなされるからです。
例えば、次のコマンドでは第 2 フィールドの 3 文字目以降でソートするよう指定していますが、第 2 フィールドの 1 文字目はタブなので、印字文字だけを見ると第 2 フィールドの 2 文字目以降でソートされることになります。
$ sort -k 2.3 sort_sample.tsv
1 baa 10
1 bab 1
10 aab 10
1 aab 5
10 bab 5
5 aab 5
1 aba 5
5 abb 1
10 abb 10
1 abb 5
印字文字の 3 文字目以降でソートしたい場合、-k 2.4
と指定するか、-t
オプションまたは -b
オプションを指定する必要があります。
$ sort -b -k 2.4 sort_sample.tsv
1 baa 10
1 aba 5
1 bab 1
5 abb 1
10 aab 10
10 abb 10
1 aab 5
1 abb 5
10 bab 5
5 aab 5
$ sort -t $'\t' -k 2.3 sort_sample.tsv
1 baa 10
1 aba 5
1 bab 1
5 abb 1
10 aab 10
10 abb 10
1 aab 5
1 abb 5
10 bab 5
5 aab 5
$ sort -b -k 2.3 sort_sample.tsv
1 baa 10
1 aba 5
1 bab 1
5 abb 1
10 aab 10
10 abb 10
1 aab 5
1 abb 5
10 bab 5
5 aab 5
まとめ
そんなわけで、-k オプションを駆使すれば第 1 フィールドは辞書式順序で昇順にソートして、第 2 フィールドは 2 文字目に以降に関して辞書式順序で降順にソートして、第 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 コマンド便利ですねっ!!!!