rcmdnk's blog

v8.10.1みたいなバージョン番号をソートしようとするとき、 単にsortコマンドとか使うとv8.2.1v8.10.1で真ん中の2101だけを 見て思ったのと逆にソートされてしまいます。

かといって単純に-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文字目が12でソート、1のうち、3文字目が数字より記号の方が優先されて、 その次がまた単体の数字でソートされて、といった感じ。 数字ソートオプション-nとかつけても変わりません。

区切ってソート

そこでsortのオプションで.で区切って分けてそれぞれで数字としてソートするようにしてみます。

出来るのはこんな感じ。

sort -n -t . -k 1.2,1 -k 2,2 -k 3,3 versions.txt

これで、上のファイルの内容がどのように並んでいても最初にあるような 期待する順番で出力されます。

各オプションは

  • -n: 数字としてソートする
  • -t: 区切り文字の指定。-t ..で区切られv121の様に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

これだけで上のソートと同じ様に期待通りのバージョンの順序にしてくれます。

ソートの方法としては数字部分を別途ひとまとめとして扱う、といういわゆるナチュラルソート(自然順) と呼ばれる方法とほぼほぼ同じですが、 単純な文字列と数値のソートだけではなくコンピューターで使われるファイル名などにより適した 改善が入っています。

Version sort ordering (GNU Coreutils)

自然順 - Wikipedia自然順 - Wikipedia

なので余程変な番号付をしてない限り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*"

とすればタグを作った順になります。

ただし、taggerdategit tag -a v1.2.3の様に注釈付き(annotated)のものだけに付いていて、 git tag v1.2.3の様に作った軽量(lightweight)版にはついてないので注意。

その場合はコミットのポインタでしかないので、そういうのも含めて日時でソートしたい場合は、

$ git tag --sort=committerdate -l "v*"

の様な形でコミットの日時でやることもできます。

Sponsored Links
  1. このあたりの歴史が もっと最近のアップデートも含めて以下のStack Overflowの答えにまとまっています。

    sorting - How to sort git tags by version string order of form rc-X.Y.Z.W? - Stack Overflow

Sponsored Links

« WSL2+Neovim+vim-submodeでmunmap_chunk(): invalid pointer Aborted iPhoneのGboard(Googleのキーボード)が文字化け »