Skip to content

/commands/semver/validate-semver-string-regex

Validate a semantic version string with a regex (bash, grep, Python)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
SemVer
versions
SemVer 2.0.0, bash 3.2+ (=~)
os
linux, macos, windows

semver.org publishes an official regular expression for SemVer 2.0.0. In bash or grep -E, which use POSIX extended regexes without \d or (?:, use the same pattern with [0-9] and plain groups, and test with [[ $version =~ $re ]]: it succeeds for 1.2.3, 1.0.0-rc.1 and 2.0.0+build.5, and fails for 1.2, v1.2.3 and 01.2.3. In Python or JavaScript, the semver.org regex works as published.

Validate and split a version in bash

bash
re='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
for v in 1.2.3 1.0.0-rc.1 2.0.0+build.5 1.2.3-01a 1.2 v1.2.3 01.2.3 1.2.3-01 1.2.3- 1.2.3+; do
  if [[ $v =~ $re ]]; then
    echo "valid    $v  major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} patch=${BASH_REMATCH[3]} pre=${BASH_REMATCH[5]} build=${BASH_REMATCH[10]}"
  else
    echo "invalid  $v"
  fi
done

Output

valid    1.2.3  major=1 minor=2 patch=3 pre= build=
valid    1.0.0-rc.1  major=1 minor=0 patch=0 pre=rc.1 build=
valid    2.0.0+build.5  major=2 minor=0 patch=0 pre= build=build.5
valid    1.2.3-01a  major=1 minor=2 patch=3 pre=01a build=
invalid  1.2
invalid  v1.2.3
invalid  01.2.3
invalid  1.2.3-01
invalid  1.2.3-
invalid  1.2.3+

BASH_REMATCH holds the capture groups: 1 to 3 are the numbers, 5 the pre-release, 10 the build metadata. Keep the regex in a variable; written inline after =~, quoted parts are matched literally.

Filter a list with grep and check with Python

bash
printf '1.2.3\nv2.0.0\n1.10.0-beta.2\n1.2\n3.0.0+20260101\n' > tags.txt
grep -E '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$' tags.txt
echo "---"
cat > semver_check.py <<'EOF'
import re, sys
SEMVER = re.compile(r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)"
                    r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)"
                    r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
                    r"(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$")
for v in sys.argv[1:]:
    m = SEMVER.match(v)
    print(v, "->", {k: x for k, x in m.groupdict().items() if x} if m else "invalid")
EOF
python semver_check.py 1.10.0-beta.2 3.0.0+20260101 v2.0.0

Output

1.2.3
1.10.0-beta.2
3.0.0+20260101
---
1.10.0-beta.2 -> {'major': '1', 'minor': '10', 'patch': '0', 'prerelease': 'beta.2'}
3.0.0+20260101 -> {'major': '3', 'minor': '0', 'patch': '0', 'buildmetadata': '20260101'}
v2.0.0 -> invalid

The Python pattern is the named-group regex from semver.org, split over several lines.

What the rules accept

VersionValid?Rule
1.2.3YesMAJOR.MINOR.PATCH, non-negative integers.
1.0.0-rc.1YesPre-release after a hyphen, dot-separated identifiers.
2.0.0+build.5YesBuild metadata after a plus; ignored for precedence.
1.2.3-01aYesAlphanumeric identifiers may start with 0.
1.2NoAll three numbers are required.
v1.2.3NoThe v prefix is not part of the version.
01.2.3 / 1.2.3-01NoNo leading zeros in numbers or numeric identifiers.
1.2.3- / 1.2.3+NoEmpty pre-release or build identifiers are not allowed.

Pitfalls

  • grep -P is not portable. The semver.org form needs PCRE (\d, (?:); GNU grep has -P, macOS grep does not. The POSIX form above works with grep -E everywhere.
  • Libraries are more lenient or stricter than the regex. npm's semver accepts and strips a leading v (semver.valid("v1.2.3") returns "1.2.3"), while Python's semver.Version.parse rejects it with ValueError: ... is not valid SemVer string.
  • PEP 440 is a different scheme. Python package versions such as 1.0rc1 or 2.0.post1 are valid for pip but not SemVer; validate them with packaging.version.Version instead.
  • Length limits. The regex has no upper bound; npm's semver rejects version strings longer than 256 characters.

FAQ

Is v1.2.3 a valid semantic version?

No. The SemVer specification says a v prefix is not part of the version; v1.2.3 is a common tag name for version 1.2.3. Strip it with ${tag#v} before validating, or use npx semver, which accepts and removes a leading v.

Why are 01.2.3 and 1.2.3-01 rejected?

SemVer forbids leading zeros in the major, minor and patch numbers and in numeric pre-release identifiers. Alphanumeric identifiers may start with a digit, so 1.2.3-01a is valid.

Can I use the regex in JavaScript?

Yes. The numbered-group regex on semver.org works in JavaScript as is. In Node projects, the semver package's semver.valid() is usually simpler and also returns the cleaned version.

Sources