sortコマンドでフィールド区切り文字を指定
先日のシェル芸勉強会福岡サテライトでちょっと話題に上がったのがsortコマンドの使い方。UNIXユーザーな方には定番の並べかえツールだが、オプションを使いこなすことでより便利な使い方が可能だ。
今回のネタも下記の本がヒントになっています。著者の上田さん、ありがとうございます。
sortコマンドでは、並べかえのキーにするフィールドを-k
オプションで指定出来る。
フィールドの区切り文字はデフォルトで連続スペースなのだが、フィールドの区切り文字を-t
オプションで別の文字に指定出来る。
使用例
今回の実行環境は、MacもしくはFreeBSDである。
10進数のIPアドレスの並べかえ
10進数のIPアドレスなどを並べ替える場合を考える。
$ cat ip_address 192.168.1.2 192.168.2.1 192.168.1.10 192.168.1.1
オプションなしでは第4オクテットが 1 -> 10 -> 2 の順になってしまう。
$ cat ip_address | sort 192.168.1.1 192.168.1.10 192.168.1.2 192.168.2.1
そこで-tオプションで.
を区切り文字にし、第3オクテットと第4オクテットの順で数値順に並べる。
$ cat ip_address | sort -t'.' -k3,3n -k4,4n 192.168.1.1 192.168.1.2 192.168.1.10 192.168.2.1
圧縮回転した過去ログの連結
FreeBSDで過去ログを時系列にzcatで連結したい場合があるが、この時もsortの-t
オプションが有効。
過去のログを1日分毎に回転しながら圧縮保存したい場合がある。FreeBSDではnewsyslogというログ回転管理のツールを使う。newsyslogの設定で、毎日0時にログを圧縮回転、最大20個分まで保存することを考える。1日分の過去ログファイルは、毎日下記のように変更されていく。
maillog.0.gz -> maillog.1.gz -> maillog.2.gz ..... maillog.9.gz -> maillog.10.gz ... -> maillog.20.gz
過去ログファイルを現在のログファイルを時系列にzcatで連結するには、下記のようなコマンドになる。
$ zcat /var/log/maillog.20.gz /var/log/maillog.19.gz .... /var/log/maillog.0.gz | cat - /var/log/maillog
しかしシェル芸やシェルスクリプトで処理する場合、この順番で過去ログファイルを並べようとすると、意外な落とし穴がある。ファイル名の数字部分が桁が揃ってないのが原因で、数字順に並んでくれないのだ。
lsコマンドの場合。maillog.1.gzの次がmaillog.10.gzになってしまう。つまり辞書順。
$ ls -l /var/log/maillog.*.bz2 -rw-r----- 1 root wheel 8098 2月 21 00:00 /var/log/maillog.0.bz2 -rw-r----- 1 root wheel 8115 2月 20 00:00 /var/log/maillog.1.bz2 -rw-r----- 1 root wheel 7923 2月 11 00:00 /var/log/maillog.10.bz2 -rw-r----- 1 root wheel 7859 2月 10 00:00 /var/log/maillog.11.bz2 -rw-r----- 1 root wheel 8296 2月 9 00:00 /var/log/maillog.12.bz2 -rw-r----- 1 root wheel 7895 2月 8 00:00 /var/log/maillog.13.bz2 -rw-r----- 1 root wheel 8438 2月 7 00:00 /var/log/maillog.14.bz2 -rw-r----- 1 root wheel 7746 2月 6 00:00 /var/log/maillog.15.bz2 -rw-r----- 1 root wheel 8298 2月 5 00:00 /var/log/maillog.16.bz2 -rw-r----- 1 root wheel 7860 2月 4 00:00 /var/log/maillog.17.bz2 -rw-r----- 1 root wheel 8365 2月 3 00:00 /var/log/maillog.18.bz2 -rw-r----- 1 root wheel 9119 2月 2 00:00 /var/log/maillog.19.bz2 -rw-r----- 1 root wheel 8055 2月 19 00:00 /var/log/maillog.2.bz2 -rw-r----- 1 root wheel 7884 2月 18 00:00 /var/log/maillog.3.bz2 -rw-r----- 1 root wheel 7712 2月 17 00:00 /var/log/maillog.4.bz2 -rw-r----- 1 root wheel 8131 2月 16 00:00 /var/log/maillog.5.bz2 -rw-r----- 1 root wheel 8338 2月 15 00:00 /var/log/maillog.6.bz2 -rw-r----- 1 root wheel 7963 2月 14 00:00 /var/log/maillog.7.bz2 -rw-r----- 1 root wheel 7726 2月 13 00:00 /var/log/maillog.8.bz2 -rw-r----- 1 root wheel 8291 2月 12 00:00 /var/log/maillog.9.bz2
シェルのファイル名展開を使った場合でも同様。辞書順。
$ echo /var/log/maillog.*.bz2 | tr ' ' '\n' /var/log/maillog.0.bz2 /var/log/maillog.1.bz2 /var/log/maillog.10.bz2 /var/log/maillog.11.bz2 /var/log/maillog.12.bz2 /var/log/maillog.13.bz2 /var/log/maillog.14.bz2 /var/log/maillog.15.bz2 /var/log/maillog.16.bz2 /var/log/maillog.17.bz2 /var/log/maillog.18.bz2 /var/log/maillog.19.bz2 /var/log/maillog.2.bz2 /var/log/maillog.20.bz2 /var/log/maillog.3.bz2 /var/log/maillog.4.bz2 /var/log/maillog.5.bz2 /var/log/maillog.6.bz2 /var/log/maillog.7.bz2 /var/log/maillog.8.bz2 /var/log/maillog.9.bz2
この場合どうするか?過去ログのファイル名には.
で区切ると第二フィールドが数字になっていることを利用。数字の降にファイル名を並べる。
$ ls /var/log/maillog.*.bz2 | sort -t'.' -k2,2nr /var/log/maillog.20.bz2 /var/log/maillog.19.bz2 /var/log/maillog.18.bz2 /var/log/maillog.17.bz2 /var/log/maillog.16.bz2 /var/log/maillog.15.bz2 /var/log/maillog.14.bz2 /var/log/maillog.13.bz2 /var/log/maillog.12.bz2 /var/log/maillog.11.bz2 /var/log/maillog.10.bz2 /var/log/maillog.9.bz2 /var/log/maillog.8.bz2 /var/log/maillog.7.bz2 /var/log/maillog.6.bz2 /var/log/maillog.5.bz2 /var/log/maillog.4.bz2 /var/log/maillog.3.bz2 /var/log/maillog.2.bz2 /var/log/maillog.1.bz2 /var/log/maillog.0.bz2
あとはxargsに渡してzcatで連結してやればいい。
$ ls /var/log/maillog.*.bz2 | sort -t'.' -k2,2nr | xargs zcat | cat - /var/log/maillog
過去14日分だけならtailコマンドなどを併用して下記のように。
$ ls /var/log/maillog.*.bz2 | sort -t'.' -k2,2nr | tail -n 14 | xargs zcat | cat - /var/log/maillog
補足
lsコマンドの-tオプション
ここまで書いておいて今更だが、lsコマンドにはファイル更新時間で並べ替える-t
オプションがある。なのでこちらを使ってもいい。
$ ls -ltr /var/log/maillog.*.bz2 -rw-r----- 1 root wheel 8691 2月 1 00:00 /var/log/maillog.20.bz2 -rw-r----- 1 root wheel 9119 2月 2 00:00 /var/log/maillog.19.bz2 -rw-r----- 1 root wheel 8365 2月 3 00:00 /var/log/maillog.18.bz2 -rw-r----- 1 root wheel 7860 2月 4 00:00 /var/log/maillog.17.bz2 -rw-r----- 1 root wheel 8298 2月 5 00:00 /var/log/maillog.16.bz2 -rw-r----- 1 root wheel 7746 2月 6 00:00 /var/log/maillog.15.bz2 -rw-r----- 1 root wheel 8438 2月 7 00:00 /var/log/maillog.14.bz2 -rw-r----- 1 root wheel 7895 2月 8 00:00 /var/log/maillog.13.bz2 -rw-r----- 1 root wheel 8296 2月 9 00:00 /var/log/maillog.12.bz2 -rw-r----- 1 root wheel 7859 2月 10 00:00 /var/log/maillog.11.bz2 -rw-r----- 1 root wheel 7923 2月 11 00:00 /var/log/maillog.10.bz2 -rw-r----- 1 root wheel 8291 2月 12 00:00 /var/log/maillog.9.bz2 -rw-r----- 1 root wheel 7726 2月 13 00:00 /var/log/maillog.8.bz2 -rw-r----- 1 root wheel 7963 2月 14 00:00 /var/log/maillog.7.bz2 -rw-r----- 1 root wheel 8338 2月 15 00:00 /var/log/maillog.6.bz2 -rw-r----- 1 root wheel 8131 2月 16 00:00 /var/log/maillog.5.bz2 -rw-r----- 1 root wheel 7712 2月 17 00:00 /var/log/maillog.4.bz2 -rw-r----- 1 root wheel 7884 2月 18 00:00 /var/log/maillog.3.bz2 -rw-r----- 1 root wheel 8055 2月 19 00:00 /var/log/maillog.2.bz2 -rw-r----- 1 root wheel 8115 2月 20 00:00 /var/log/maillog.1.bz2 -rw-r----- 1 root wheel 8098 2月 21 00:00 /var/log/maillog.0.bz2
しかしうっかりファイル更新時間を変えてしまうことも考えられるので、sortでファイル名並べかえの方がいい気がする。
lsコマンドで更新時刻を出力
下記の記事で書いたように、ファイル更新日付を固定長で桁を揃えて出力し、並べかえのキーとして使う手もある。
GNU sortコマンドの-Vオプション
GNU版sortコマンド限定だが、バージョン名で並べ替える-V
オプションがある。ピリオド区切りの数字をバージョン名として認識して並べかえてくれるようだ。
$ cat version 1.3 2.1 1.1.12 1.1.9 1.1 1.2 $ cat version | sort 1.1 1.1.12 1.1.9 1.2 1.3 2.1 $ cat version | gsort -V 1.1 1.1.9 1.1.12 1.2 1.3 2.1
これを使うと手っ取り早いかも。
$ cat ip_address | gsort -V 192.168.1.1 192.168.1.2 192.168.1.10 192.168.2.1 $ ls /var/log/maillog.*.bz2 | gsort -Vr /var/log/maillog.20.bz2 /var/log/maillog.19.bz2 /var/log/maillog.18.bz2 /var/log/maillog.17.bz2 /var/log/maillog.16.bz2 /var/log/maillog.15.bz2 /var/log/maillog.14.bz2 /var/log/maillog.13.bz2 /var/log/maillog.12.bz2 /var/log/maillog.11.bz2 /var/log/maillog.10.bz2 /var/log/maillog.9.bz2 /var/log/maillog.8.bz2 /var/log/maillog.7.bz2 /var/log/maillog.6.bz2 /var/log/maillog.5.bz2 /var/log/maillog.4.bz2 /var/log/maillog.3.bz2 /var/log/maillog.2.bz2 /var/log/maillog.1.bz2 /var/log/maillog.0.bz2
上記の例以外でも、フィールド長が変わるSNMPのMIBオブジェクトの並べかえにも便利。