/commands/semver/validate-semver-string-regex
Validate a semantic version string with a regex (bash, grep, Python)
- 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
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
doneOutput
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
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.0Output
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 -> invalidThe Python pattern is the named-group regex from semver.org, split over several lines.
What the rules accept
| Version | Valid? | Rule |
|---|---|---|
| 1.2.3 | Yes | MAJOR.MINOR.PATCH, non-negative integers. |
| 1.0.0-rc.1 | Yes | Pre-release after a hyphen, dot-separated identifiers. |
| 2.0.0+build.5 | Yes | Build metadata after a plus; ignored for precedence. |
| 1.2.3-01a | Yes | Alphanumeric identifiers may start with 0. |
| 1.2 | No | All three numbers are required. |
| v1.2.3 | No | The v prefix is not part of the version. |
| 01.2.3 / 1.2.3-01 | No | No leading zeros in numbers or numeric identifiers. |
| 1.2.3- / 1.2.3+ | No | Empty pre-release or build identifiers are not allowed. |
Pitfalls
grep -Pis not portable. The semver.org form needs PCRE (\d,(?:); GNU grep has-P, macOS grep does not. The POSIX form above works withgrep -Eeverywhere.- 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'ssemver.Version.parserejects it with ValueError: ... is not valid SemVer string. - PEP 440 is a different scheme. Python package versions such as
1.0rc1or2.0.post1are valid for pip but not SemVer; validate them withpackaging.version.Versioninstead. - 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.