文字列からバージョン情報だけを抜き出す正規表現(bash/dash/ash/zsh で grep)


v2.1.0-39-g4e1ede1 から 2.1.0 もしくは v2.1.0 のみを抜き出したい。可能ならメジャー、マイナー、パッチのバージョンも個別に。

... ... grep だっけ? sed だっけ?しかして awk

意外なことに「bash 正規表現 バージョン情報 抜き出す」でググってみても、文字列からバージョン番号を抜き出すだけ・・の記事がなかったので自分のググラビリティとして。

TL; DR (今北産業)

grep-o オプションで正規表現にマッチした文字のみ抜き出し、-E オプションで細かい指定をし、| head で最初にマッチしたもののみに絞る。

grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1
例(vなし)
$ echo "v2.1.0-39-g4e1ede1" | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1
2.1.0
例(vつき)
$ echo "v2.1.0-39-engine-1.2.3" | grep -o -E "(v[0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1
v2.1.0
応用(メジャー、マイナー、パッチバージョンを抜き出す)
$ MyVersion=$(echo "v2.1.0-39-engine-1.2.3" | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1)
$ echo $MyVersion | awk -F. '{printf "Major:%d, Minor:%d, Patch:%d\n", $1,$2,$3}'
Major:2, Minor:1, Patch:0

TS; DR

sample.sh(色々なパターンをテスト)
#!/bin/bash

test() {
    input="$1"
    expect="$2"

    actual=$(echo "${input}" | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1)

    [ "$expect" = "$actual" ] && {
        printf '%s' '[OK] '
    } || {
        printf '%s' '[ng] '
    }

    printf "Input: %s\t-> Output: %s\n" "$input" "$actual"
}

#    Input        Expect
test "2"          ""
test ".2"         ""
test "..2"        ""
test "2.1"        "2.1"
test "2..1"       ""
test "version2"   ""
test "version2.1" "2.1"
test "22.11"      "22.11"
test "2.1.0"      "2.1.0"
test "2.1.10"     "2.1.10"
test "2.10.0"     "2.10.0"
test "22.1.0"     "22.1.0"
test "33.22.11"   "33.22.11"

test "v1.2-3.4.5.6.7"     "1.2"
test "v1.2.3.4.5.6.7.8"   "1.2.3"
test "v1.2.3-4.5.6-7"     "1.2.3"
test "2.1.0-39-g4e1ede"   "2.1.0"
test "v2.1.0-39-g4e1ede1" "2.1.0"

grep --version | head -n1

テスト結果
$ ./sample.sh
[OK] Input: 2    -> Output: 
[OK] Input: .2  -> Output: 
[OK] Input: ..2 -> Output: 
[OK] Input: 2.1 -> Output: 2.1
[OK] Input: 2..1        -> Output: 
[OK] Input: version2    -> Output: 
[OK] Input: version2.1  -> Output: 2.1
[OK] Input: 22.11       -> Output: 22.11
[OK] Input: 2.1.0       -> Output: 2.1.0
[OK] Input: 2.1.10      -> Output: 2.1.10
[OK] Input: 2.10.0      -> Output: 2.10.0
[OK] Input: 22.1.0      -> Output: 22.1.0
[OK] Input: 33.22.11    -> Output: 33.22.11
[OK] Input: v1.2-3.4.5.6.7      -> Output: 1.2
[OK] Input: v1.2.3.4.5.6.7.8    -> Output: 1.2.3
[OK] Input: v1.2.3-4.5.6-7      -> Output: 1.2.3
[OK] Input: 2.1.0-39-g4e1ede    -> Output: 2.1.0
[OK] Input: v2.1.0-39-g4e1ede1  -> Output: 2.1.0
grep (GNU grep) 3.4

応用技 (メジャー、マイナー、パッチバージョンを抜き出す)

xx.yy.zzセマンティック・バージョニングなら awk で抜き出せます。

awk -F. '{printf "Major:%d, Minor:%d, Patch:%d\n", $1,$2,$3}'

Golangのメジャー、マイナー、パッチバージョンを抜き出す例
$ go version
go version go1.16.3 linux/amd64

$ GoVersion=$(go version | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" | head -n1)
$ echo $GoVersion | awk -F. '{printf "Major:%d, Minor:%d, Patch:%d\n", $1,$2,$3}'
Major:1, Minor:16, Patch:3

参考文献/あわせて読みたい

  • どのUNIXコマンドでも使える正規表現 @ Qiita
    • たった3つの正規表現メタ文字セットだけ知ればいい
      抜本的に正規表現を理解出来る、目から鱗の Qiita 記事です。Docker の Alpine でゴニョゴニョすることが多い人は POSIX 原理主義者になった方が良いと思いました。