2 番目のフィールドでのソート

2 列目で、並べ替えをするには?に 2 番目のフィールドでのソートについて質問されていますが、awk の場合には決して簡単なことではありません。

最初に nawk のような sort 関数を持っていないものを awk で作成したものを示します。 さらにここではマージソートを使っていますが、そのマージソートの中の比較している部分を直接変更したものを載せます。

#! /usr/bin/gawk -f
# 2nd_field_sort.awk

{
    line[NR] = $0;
}

END {
    merge_sort(line, 1, NR, 2);
    for (i = 1; i <= NR; i++) {
        print line[i];
    }
}

#---------------------------------------------------------------------
# function for recursive merge sort
function merge_sort(arr, a, b, key,   k) {
    if (a < b) {
        k = int((a + b) / 2);
        merge_sort(arr, a, k, key);
        merge_sort(arr, k + 1, b, key);
        merge(arr, a, k, b, key);
    }
}

#---------------------------------------------------------------------
# function for merge
function merge(arr, a, k, b, key,   i, j, p, c) {
    j = i = a;
    p = k + 1;
    while (a <= k && p <= b) {
        split(arr[a], arr_key1);
        split(arr[p], arr_key2);
        if (arr_key1[key] <= arr_key2[key]) {
            c[i++] = arr[a++];
        } else {
            c[i++] = arr[p++];
        }
    }
    while (a <= k) {
        c[i++] = arr[a++];
    }
    while (p <= b) {
        c[i++] = arr[p++];
    }
    while (j <= b) {
        arr[j] = c[j];
        j++;
    }
}

さて、これを実行してみます。

$ cat sample.txt
AA 55 AA AA
BB 44 BB BB
CC 33 CC CC
DD 22 DD DD
EE 11 EE EE
$ gawk -f 2nd_field_sort.awk sample.txt
EE 11 EE EE
DD 22 DD DD
CC 33 CC CC
BB 44 BB BB
AA 55 AA AA

確かに要望どおりにソートされているようです。 自分で sort 関数を全て記述している分、自由度は非常に高いのですが、マージソートを間違えることなく何も見ずに記述するのは大変です。

そこで gawk の asort() 関数を使って簡略化してみましょう。 ただし、asort() 関数にはどの項目をキーとしてソートするかというようなきめ細かな設定ができません。 そこで、asort() に渡す配列に小細工をしてやります。

#! /usr/bin/gawk -f
# 2nd_field_sort2.awk

{
    line[NR] = $2 "\t" $0;
}

END {
    asort(line);
    for (i = 1; i <= NR; i++) {
        print substr(line[i], index(line[i], "\t") + 1);
    }
}

つまりソートのキーに相当する部分を先頭に持ってきて、対象文字列で使われていない文字を区切りにしておいて、最後に分割しています。 ここでは TAB にしていますが、SUBSEP などのようが良いかもしれません。 実際に実行してみます。

$ cat sample.txt
AA 55 AA AA
BB 44 BB BB
CC 33 CC CC
DD 22 DD DD
EE 11 EE EE
$ gawk -f 2nd_field_sort.awk sample.txt
EE 11 EE EE
DD 22 DD DD
CC 33 CC CC
BB 44 BB BB
AA 55 AA AA

tag_gawk.pngtag_gawk.png