awk, sortコマンドでCSVの囲み文字付き数値フィールドを並び替える

CSVで、数値フィールドがダブルクォーテーションなどの囲み文字で囲まれていると、sortコマンドだけでは数値としての並び替えができない。
awkを使って数値フィールドの囲み文字を除去してからsortを実行し、その後数値フィールドを再び囲み文字で囲むことを考える。
※上記処理の対象を数値フィールドだけでなく全てのフィールドを対象にすることで期待結果が得られれば事は単純。
しかし、フィールドデータにカンマが含まれていると問題になりうるのでこの方法は採用しない。データにカンマが含まれないことが担保できれば、その方法でも良いが。

以下のファイルで試してみる。

1$ cat sample.txt
2"123","ab12c","9","a"
3"9875","w{z","8","b"
4"58","12zn9p","7","c"
5"45","@@","7","d"
6"741","i59:::","8","e"
7"1657","c\^-","9","f"

3列目を昇順、1列目を降順で並び替えてみた。

1$ awk -F, -v OFS=, '{gsub(/\"/, "", $1); gsub(/\"/, "", $3); print}' sample.txt | sort -t, -k 3,3n -k 1,1nr | awk -F, -v OFS=, '{sub($1, "\"&\"", $1); sub($3, "\"&\"", $3); print}'
2"58","12zn9p","7","c"
3"45","@@","7","d"
4"9875","w{z","8","b"
5"741","i59:::","8","e"
6"1657","c\^-","9","f"
7"123","ab12c","9","a"

◆解説
最初のawkコマンドについて。
-F, はセパレータがカンマであることを指定。
-v OFS=, は出力結果のセパレータにカンマを指定(これを指定しないとデフォルトのスペースで出力されてしまう)。
並び替えキーの1列目と3列目を数値として扱うため、gsubで一旦囲み文字のダブルクォーテーションを削除している。
printで、囲み文字除去後のレコードを出力。
このawkの出力結果は以下となる。

1$ awk -F, -v OFS=, '{gsub(/\"/, "", $1); gsub(/\"/, "", $3); print}' sample.txt
2123,"ab12c",9,"a"
39875,"w{z",8,"b"
458,"12zn9p",7,"c"
545,"@@",7,"d"
6741,"i59:::",8,"e"
71657,"c\^-",9,"f"

上の出力結果をパイプでsortコマンドに渡す。sortコマンドについて。
-t, はセパレータがカンマであることを指定。
-k 3,3n は3列目を数値と見做して昇順に並び替え。
-k 1,1nr は1列目を数値と見做して降順に並び替え。
このsortの出力結果は以下となる。

1$ awk -F, -v OFS=, '{gsub(/\"/, "", $1); gsub(/\"/, "", $3); print}' sample.txt | sort -t, -k 3,3n -k 1,1nr
258,"12zn9p",7,"c"
345,"@@",7,"d"
49875,"w{z",8,"b"
5741,"i59:::",8,"e"
61657,"c\^-",9,"f"
7123,"ab12c",9,"a"

最後に上の出力結果をawkコマンドに渡し、一旦除去していた囲み文字を付与する。
sub($1, ""&"", $1)で1列目を囲み文字で挟んで置換する。&が一致した文字列に置き換えられる。
3列目も同様。
うっかり最後の引数を指定せずsub($3, ""&"")とすると、置換対象がレコード全体となり、3列目よりも前に3列目と同じ文字列があるとそこがヒットしてしまい3列目が置換されないので、要注意。
置換後にprintでレコード出力。
出力結果を再度載せておく。

1$ awk -F, -v OFS=, '{gsub(/\"/, "", $1); gsub(/\"/, "", $3); print}' sample.txt | sort -t, -k 3,3n -k 1,1nr | awk -F, -v OFS=, '{sub($1, "\"&\"", $1); sub($3, "\"&\"", $3); print}'
2"58","12zn9p","7","c"
3"45","@@","7","d"
4"9875","w{z","8","b"
5"741","i59:::","8","e"
6"1657","c\^-","9","f"
7"123","ab12c","9","a"

◆参考
awkコマンド:http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0315.HTM
sortコマンド:http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0337.HTM

関連ページ