v8.10.1
みたいなバージョン番号をソートしようとするとき、
単にsort
コマンドとか使うとv8.2.1
とv8.10.1
で真ん中の2
と10
の1
だけを
見て思ったのと逆にソートされてしまいます。
かといって単純に-n
で数字として全体を見ることもできないのでちょっと工夫が必要です。
sortコマンド
v1.2.1
v1.10.1
v2.2.1
v2.2.2
v2.2.10
v10.2.1
みたいな内容のversions.txtというフィアルの中身をソートしたいとき、 期待するのは上の形になることです。
これをそのままsort
コマンドとかに入れると
$ sort versions.txt
v1.10.1
v1.2.1
v10.2.1
v2.2.1
v2.2.10
v2.2.2
となります。
このままだとv
から始まって.
とかも含まれる文字列なので
単に2文字目が1
か2
でソート、1
のうち、3文字目が数字より記号の方が優先されて、
その次がまた単体の数字でソートされて、といった感じ。
数字ソートオプション-n
とかつけても変わりません。
区切ってソート
そこでsort
のオプションで.
で区切って分けてそれぞれで数字としてソートするようにしてみます。
出来るのはこんな感じ。
sort -n -t . -k 1.2,1 -k 2,2 -k 3,3 versions.txt
これで、上のファイルの内容がどのように並んでいても最初にあるような 期待する順番で出力されます。
各オプションは
-n
: 数字としてソートする-t
: 区切り文字の指定。-t .
で.
で区切られv1
、2
、1
の様に3つのフィールドに分けられます。この指定がない場合は区切り文字はスペースになります。-k
: ソートに用いる箇所の指定。- 複数指定でき、先に指定した方が優先される。各区切りは左から1から数字が振られる。
- 各指定は
<開始フィールド>[フィールド中の開始文字][オプション][,終了フィールド[終了文字][オプション]]
- まず使うフィールドの指定は必須。(フィールド番号は左から1から始まる。)
.
の後に数字を書くとフィールド中の何文字目から、という指定が出来る。(フィールドの最初が1、指定がなければ1と同じ。)- 終了側は文字指定がなければ指定したフィールドの最後までを見る。
- 終了フィールドを指定しないと見る行の最後まで、になる。
- したがって、
-k 2 -k 3
とかは意味が無い(-k 2
で3つ目のフィールド部分も含まれてしまうため)
- したがって、
- オプションはそのフィールドだけに適用したいオプションを指定する。(
-k2,2r
と指定すればその箇所だけ逆順でソートする)
といった感じ。
使っているファイルはvX.Y.Z
の形のバージョン情報を持っているので、
X
, Y
, Z
の順でソートしたいわけですが、
.
で区切ると1つ目にはv
が付くので最初のものだけ、-k 1.2,1
で
1つ目のフィールドの2番目の文字からそのフィールドの最後まで、という指定をしています。
sort -V
と、sort
コマンドで頑張っては居ますが、実はsort
コマンドには自動でバージョン記述を
把握してソートしてくれる機能があります。
$ sort -V versions.txt
これだけで上のソートと同じ様に期待通りのバージョンの順序にしてくれます。
ソートの方法としては数字部分を別途ひとまとめとして扱う、といういわゆるナチュラルソート(自然順) と呼ばれる方法とほぼほぼ同じですが、 単純な文字列と数値のソートだけではなくコンピューターで使われるファイル名などにより適した 改善が入っています。
なので余程変な番号付をしてない限りsort -V
でいい感じにソートできるはずです。
(逆にこれでうまくソートできないものはバージョンの付け方を考え直した方が良いかも。)
上のリファレンスもGNUのものになってますが、
最初にGNU版sortにこの-V
(--version-sort
)というオプション
があり、
BSD版にはしばらくなかったみたいですが、
現在はBSD版にも入っていて、macOSに入っているバージョンのsortでも使える様になっています。
git tagでの表示
最近使う中で、バージョン一覧、みたいなものを一番良く表示するのはgit tag
コマンドです。
git tag
で表示させてから、sort
コマンドを使って、
$ git tag | sort -V
でバージョン順にソートできます。
ただ、sort
を使わなくても、Git 2.0からは
$ git tag --sort=version:refname
とすることでtagの出力を直接sort -V
の様な期待通りの順に出してくれます。
version
は--sort=v:refname
の様に省略することもできます。
このソートで逆順にしたい場合は
$ git tag --sort=-version:refname
の様にソートのやり方の最初にマイナス記号を付けます。
常にgit tag
で上の様なバージョンとしてソートした上で表示したい場合、
$ git config --global tag.sort version:refname
とかして、~/.gitconfigに
[tag]
sort = version:refname
という行を追加しておくとgit tag
のデフォルトのソート方法がバージョン形式になります。
(この機能はGit 2.1から。)
1
もし、tagの中に色々とバージョン名以外のものも含まれている場合、
$ git tag --sort=version:refname -l "v*"
の様な形で正規表現を渡してそれに沿ったもの(この場合は、v
で始まる)tagだけ表示することもできます。
ここで利用できる正規表現はシェル的なものになります。
(*
で全て0字以上の全ての文字列に対応。)
Gitの機能であれば、単純にタグ名だけでソートするだけでなく、 タグを作った日時でもソートでき、
$ git tag --sort=taggerdate -l "v*"
とすればタグを作った順になります。
ただし、taggerdate
はgit tag -a v1.2.3
の様に注釈付き(annotated
)のものだけに付いていて、
git tag v1.2.3
の様に作った軽量(lightweight
)版にはついてないので注意。
その場合はコミットのポインタでしかないので、そういうのも含めて日時でソートしたい場合は、
$ git tag --sort=committerdate -l "v*"
の様な形でコミットの日時でやることもできます。
-
このあたりの歴史が もっと最近のアップデートも含めて以下のStack Overflowの答えにまとまっています。
sorting - How to sort git tags by version string order of form rc-X.Y.Z.W? - Stack Overflow ↩