-
-
Save talkingmoose/2cf20236e665fcd7ec41311d50c89c0e to your computer and use it in GitHub Desktop.
#!/bin/bash | |
<<ABOUT_THIS_SCRIPT | |
------------------------------------------------------------------------------- | |
Written by:William Smith | |
Professional Services Engineer | |
Jamf | |
bill@talkingmoose.net | |
https://gist.github.com/2cf20236e665fcd7ec41311d50c89c0e | |
Originally posted: April 12, 2020 | |
Modified: May 6, 2020 | |
Changes: | |
Adding support to break long regex strings for Jamf Pro. | |
Modified: May 24, 2020 | |
Changes: | |
Displaying sequence characters in verbose reporting instead of number. | |
Now accounting for version strings with non-numeric characters. | |
Added warning if sequence begins with "0". | |
Added warning if sequence contains non-standard characters. | |
Accounting for multple Jamf Pro regex strings. | |
Purpose: Generate a regular expression (regex) string that matches | |
the provided version number or higher. | |
Instructions: Run the script in Terminal, supplying a version number | |
string as the first argument: | |
e.g. '/path/to/Match Version Number or Higher.bash' 16.17 | |
Or run the script in Terminal without any argument to use the example | |
version number string within the script. | |
Optionally, set verbose to "On" or "Off". | |
Except where otherwise noted, this work is licensed under | |
http://creativecommons.org/licenses/by/4.0/ | |
"Perhaps it is the forgetting not the remembering that is the essence | |
of what makes us human. To make sense of the world, we must filter it." | |
------------------------------------------------------------------------------- | |
ABOUT_THIS_SCRIPT | |
# ----- set verbosity and provide a version number string --------------------- | |
# turn on for step-by-step explanation while building the regex or off to provide only the regex | |
verbose="On" # "On" or "Off" | |
usingJamf="Yes" # "Yes" or "No" | |
# from supplied argument in Terminal | |
versionString=$1 | |
# confirm version string only contains numbers and periods or is blank | |
if [[ $versionString =~ [^[:digit:].] ]]; then | |
warning="Yes" | |
fi | |
# sample version strings | |
if [[ "$versionString" = "" ]]; then | |
# versionString="79.0.3945.117" # e.g. Google Chrome | |
# versionString="16.17" # Microsoft Office 2019 | |
# versionString="74.0.1" # Mozilla Firefox | |
# versionString="19.10.2.41" # Citrix Workspace | |
# versionString="20.006.20034" # Adobe Acrobat Reader DC | |
# versionString="19.021.20058" # Adobe Acrobat Pro DC | |
# versionString="21.0.3" # Adobe Photoshop 2020 | |
# versionString="100.86.91" # Microsoft Defender | |
# versionString="5.0.3 (24978.0517)" # Zoom.us | |
versionString="5.0.3-24978.0517 (4323)" # just a long and complicated test string | |
fi | |
# ----- functions ------------------------------------------------------------- | |
# enables or disables verbose mode | |
function logcomment() { | |
if [[ "$verbose" = "On" ]]; then | |
echo "$1" | |
fi | |
} | |
# processes a digit within a sequence | |
function evaluateSequence() { | |
# ----- process the first digit in a sequence ----------------------------- | |
# prepend exact characters leading up to the current character under evaluation | |
if [[ "$regex" != "" ]]; then | |
regex="$regex|" | |
fi | |
# get the sequence ( e.g. "74" of "74.0.1" ) | |
sequence=$( /usr/bin/awk -F "." -v i=$aSequence '{ print $i }' <<< "$adjustedVersionString" ) | |
logcomment "Sequence $aSequence is \"$sequence\"" | |
# show warning if sequence begins with "0" | |
if [[ "$sequence" =~ ^0.+ ]]; then | |
warning="Yes" | |
fi | |
# get count of digits in the sequence ( e.g. 2 digits in "74" ) | |
digitCount=$( /usr/bin/tr -d '\r\n' <<< "$sequence" | /usr/bin/wc -c | /usr/bin/xargs ) # e.g. 2 | |
logcomment "Count of digits in sequence \"$sequence\" is $digitCount" | |
logcomment | |
# generate regex for the first number of the sequence rolling over to add another digit ( e.g. 99 > 100 ) | |
logcomment "Count of digits in sequence \"$sequence\" may roll over to $((digitCount + 1)) or more digits" | |
buildRegex="$regexPrefix\d{$((digitCount + 1)),}" | |
logcomment "Regex for $((digitCount + 1)) or more digits is \"$buildRegex\"" | |
# add a wildcard to end of string to match everything else | |
logcomment "Wildcard everything else" | |
buildRegex="$buildRegex.*" | |
# show complete regex for this digit | |
logcomment "Complete regex is \"$buildRegex\"" | |
regex="$regex$buildRegex" | |
# show the entire regex as the script progresses through each digit | |
logcomment "Progressive regex: $regex" | |
logcomment | |
# ----- process the remaining digits in a sequence ------------------------ | |
# create array of digits in sequence ( e.g. "7, 4" ) | |
digits=() | |
for ((i = 0; i < ${#sequence}; i++)); do | |
digits+=(${sequence:$i:1}) | |
done | |
# iterate over each digit of the sequence | |
# for aDigit in ${digits[*]} | |
for indexNumber in "${!digits[@]}" | |
do | |
# ----- the number 8 can only roll up to 9 ---------------------------- | |
if [[ "${digits[$indexNumber]}" -eq 8 ]]; then | |
logcomment "Because digit $((indexNumber + 1 )) in sequence \"$sequence\" is \"8\", roll it to \"9\"" | |
buildRegex="9" | |
if [[ $((digitCount - indexNumber - 1 )) -ne 0 ]]; then | |
logcomment "Because remaining count of digits in sequence \"$sequence\" is $((digitCount - indexNumber - 1 )), pad the sequence with $((digitCount - indexNumber - 1 )) more digit(s)" | |
buildRegex="$buildRegex\d{$((digitCount - indexNumber - 1 )),}" | |
logcomment "Regex for $((digitCount - indexNumber - 1 )) more digit(s) is \d{$((digitCount - indexNumber - 1 )),}" | |
fi | |
logcomment "Wildcard everything else" | |
buildRegex="$regexPrefix$buildRegex.*" | |
logcomment "Complete regex is \"$buildRegex\"" | |
logcomment "Progressive regex: $regex|$buildRegex" | |
regex="$regex|$buildRegex" | |
logcomment | |
# ----- anything 0 through 7 will roll up to the next number ---------- | |
elif [[ "${digits[$indexNumber]}" -lt 8 ]]; then | |
logcomment "Because digit $((indexNumber + 1 )) in sequence \"$sequence\" is \"${digits[$indexNumber]}\", roll it to \"$((${digits[$indexNumber]} + 1))\" or higher" | |
buildRegex="[$((${digits[$indexNumber]} + 1))-9]" | |
logcomment "Regex for $((${digits[$indexNumber]} + 1)) or higher is \"$buildRegex\"" | |
if [[ $((digitCount - indexNumber - 1 )) -ne 0 ]]; then | |
logcomment "Because remaining count of digits in sequence \"$sequence\" is $((digitCount - indexNumber - 1 )), pad the sequence with $((digitCount - indexNumber - 1 )) more digit(s)" | |
buildRegex="$buildRegex\d{$((digitCount - indexNumber - 1 )),}" | |
logcomment "Regex for $((digitCount - indexNumber - 1 )) more digit(s) is \d{$((digitCount - indexNumber - 1 )),}" | |
fi | |
logcomment "Wildcard everything else" | |
buildRegex="$regexPrefix$buildRegex.*" | |
logcomment "Complete regex is \"$buildRegex\"" | |
logcomment "Progressive regex: $regex|$buildRegex" | |
regex="$regex|$buildRegex" | |
logcomment | |
# ----- nothing to do if the digit is 9 ------------------------------- | |
# ----- (the preceding digit is already rolled up) -------------------- | |
else | |
logcomment "Because \"Digit $((indexNumber + 1 ))\" in sequence \"$sequence\" is 9, do nothing" | |
logcomment | |
fi | |
regexPrefix="$regexPrefix${digits[$indexNumber]}" | |
done | |
} | |
# ----- run the script -------------------------------------------------------- | |
# verify the version string to the user | |
logcomment "Version string is $versionString" | |
# replace non-numeric sequences of characters with periods | |
adjustedVersionString=$( /usr/bin/sed -E 's/[^0-9]+/./g' <<< "$versionString" | /usr/bin/sed -E 's/[^0-9]$//g' ) | |
logcomment "Adjusted version string for parsing is \"$adjustedVersionString\"" | |
# number of "sequences" separated by a divider | |
sequenceCount=$( /usr/bin/awk -F "." '{ print NF }' <<< "$adjustedVersionString" ) # e.g. 4 | |
logcomment "Number of sequences is $sequenceCount" | |
# create a list of sequence dividers in the version string separated by "###" | |
sequenceDividers=$( /usr/bin/sed -E 's/[0-9]+/###/g' <<< "$versionString" ) | |
logcomment "Replacing digits in sequences to get the sequence dividers \"$sequenceDividers\"" | |
logcomment | |
# 14 special regex characters that may appear as sequence dividers that will need escaping | |
regexSpecialCharacters="\&$.|?*+()[]{}" | |
# used to track unchanged digits to the left of the current digit being evaluated | |
regexPrefix="" | |
# evaluate the version string | |
for ((aSequence=1;aSequence<=$sequenceCount;aSequence++)) | |
do | |
logcomment "Evaluating sequence $aSequence of $sequenceCount" | |
evaluateSequence | |
# resetting variable | |
dividers="" | |
# add sequence divider to end of the sequence | |
divider=$( /usr/bin/awk -F "###" -v divider=$(( aSequence + 1 )) '{ print $divider }' <<< "$sequenceDividers" ) | |
for (( aCharacter=0; aCharacter<${#divider}; aCharacter++ )) | |
do | |
logcomment "Next character is \"${divider:$aCharacter:1}\"" | |
if [[ "$regexSpecialCharacters" = *"${divider:$aCharacter:1}"* ]]; then | |
dividers="$dividers\\${divider:$aCharacter:1}" | |
logcomment "Escaping \"${divider:$aCharacter:1}\" to create \"\\${divider:$aCharacter:1}\"" | |
else | |
dividers="$dividers${divider:$aCharacter:1}" | |
logcomment "This character does not need escaping" | |
fi | |
done | |
regexPrefix="$regexPrefix$dividers" | |
logcomment "Progressive regex: $regex|$regexPrefix" | |
logcomment | |
done | |
# include original version string at end of regex, escaping special regex characters | |
escapedVersionString="" | |
for (( aCharacter=0; aCharacter<${#versionString}; aCharacter++ )) | |
do | |
if [[ "$regexSpecialCharacters" = *"${versionString:$aCharacter:1}"* ]]; then | |
escapedVersionString="$escapedVersionString\\${versionString:$aCharacter:1}" | |
else | |
escapedVersionString="$escapedVersionString${versionString:$aCharacter:1}" | |
fi | |
done | |
regex="$regex|$escapedVersionString" | |
logcomment "Adding original version string to end of regex as a potential match." | |
logcomment | |
if [[ "$warning" = "Yes" ]]; then | |
echo | |
echo "===============================================" | |
echo " " | |
echo " WARNING " | |
echo " " | |
echo " This version string contains non-standard " | |
echo " characters or number sequences that begin " | |
echo " with a zero (i.e. \"0123\", which is the " | |
echo " same as \"123\"). " | |
echo " " | |
echo " Use regexes with caution. " | |
echo " " | |
echo "===============================================" | |
echo | |
fi | |
# return full regex including start and end of string characters (e.g. ^ and $ ) | |
regex="^($regex.*)$" | |
# get characterCount of regex | |
regexCharacterCount=$( /usr/bin/wc -c <<< "$regex" | /usr/bin/xargs ) | |
# display the regex for the version string and its character count | |
echo | |
echo "Regex for \"$versionString\" or higher ($regexCharacterCount characters): | |
$regex" | |
echo | |
if [[ "$usingJamf" = "Yes" ]] && [[ "$regexCharacterCount" -gt 255 ]]; then | |
# get count of characters in generated regex string | |
regexCharacters=${#regex} | |
# determine number of regex strings needed, accounting for beginning ^ and ending $ characters | |
jamfStringCount="$((regexCharacters / 254 + 1))" | |
# get number of sequences separated by | in regex | |
sequenceCount=$( /usr/bin/awk -F "|" '{ print NF }' <<< "$regex" ) | |
# divide the count of sequences in half | |
breakDelimiterPosition=$((sequenceCount / jamfStringCount)) | |
# replace middle | operator(s) with the letter "b" | |
dividedRegex="$regex" | |
for (( aBreak=0; aBreak<$jamfStringCount; aBreak++ )) | |
do | |
breakDelimiterPosition=$((breakDelimiterPosition * aBreak + breakDelimiterPosition)) | |
dividedRegex=$( /usr/bin/sed "s/|/b/$breakDelimiterPosition" <<< "$dividedRegex" ) | |
done | |
# print Jamf Pro instructions and both regex strings | |
echo | |
echo "Jamf Pro has a field character limit of 255 characters." | |
echo "This regex exceeds that field character limit." | |
echo "Add additional \"Application Version\" criteria to your search" | |
echo "and paste each regex string into the the additional fields." | |
echo | |
echo | |
echo "For example:" | |
echo | |
echo " Application Title is Google Chrome.app" | |
echo "and ( Application Version matches regex <Regex 1>" | |
echo "or Application Version matches regex <Regex 2> )" | |
echo | |
echo | |
# display each Jamf Pro string | |
for (( aBreak=0; aBreak<$jamfStringCount; aBreak++ )) | |
do | |
regexString=$( /usr/bin/awk -F "b" -v divider=$(( aBreak + 1 )) '{ print $divider }' <<< "$dividedRegex" ) | |
# add beginning of line characters if needed | |
if [[ "$regexString" != "^("* ]]; then | |
regexString="^($regexString" | |
fi | |
# add end of line characters if needed | |
if [[ "$regexString" != *")$" ]]; then | |
regexString="$regexString)$" | |
fi | |
# display each regex string | |
echo "Regex $((aBreak + 1)):" | |
echo "$regexString" | |
echo | |
done | |
fi | |
exit 0 |
Regex for "79.0.3945.117" or higher (249 characters): | |
^(\d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.*|79\.0\.3945\.1[2-9]\d{1,}.*|79\.0\.3945\.11[8-9].*|79\.0\.3945\.117.*)$ |
Version string is 79.0.3945.117 | |
Adjusted version string for parsing is "79.0.3945.117" | |
Number of sequences is 4 | |
Replacing digits in sequences to get the sequence dividers "###.###.###.###" | |
Evaluating sequence 1 of 4 | |
Sequence 1 is "79" | |
Count of digits in sequence "79" is 2 | |
Count of digits in sequence "79" may roll over to 3 or more digits | |
Regex for 3 or more digits is "\d{3,}" | |
Wildcard everything else | |
Complete regex is "\d{3,}.*" | |
Progressive regex: \d{3,}.* | |
Because digit 1 in sequence "79" is "7", roll it to "8" or higher | |
Regex for 8 or higher is "[8-9]" | |
Because remaining count of digits in sequence "79" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "[8-9]\d{1,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.* | |
Because "Digit 2" in sequence "79" is 9, do nothing | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\. | |
Evaluating sequence 2 of 4 | |
Sequence 2 is "0" | |
Count of digits in sequence "0" is 1 | |
Count of digits in sequence "0" may roll over to 2 or more digits | |
Regex for 2 or more digits is "79\.\d{2,}" | |
Wildcard everything else | |
Complete regex is "79\.\d{2,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.* | |
Because digit 1 in sequence "0" is "0", roll it to "1" or higher | |
Regex for 1 or higher is "[1-9]" | |
Wildcard everything else | |
Complete regex is "79\.[1-9].*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].* | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\. | |
Evaluating sequence 3 of 4 | |
Sequence 3 is "3945" | |
Count of digits in sequence "3945" is 4 | |
Count of digits in sequence "3945" may roll over to 5 or more digits | |
Regex for 5 or more digits is "79\.0\.\d{5,}" | |
Wildcard everything else | |
Complete regex is "79\.0\.\d{5,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.* | |
Because digit 1 in sequence "3945" is "3", roll it to "4" or higher | |
Regex for 4 or higher is "[4-9]" | |
Because remaining count of digits in sequence "3945" is 3, pad the sequence with 3 more digit(s) | |
Regex for 3 more digit(s) is \d{3,} | |
Wildcard everything else | |
Complete regex is "79\.0\.[4-9]\d{3,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.* | |
Because "Digit 2" in sequence "3945" is 9, do nothing | |
Because digit 3 in sequence "3945" is "4", roll it to "5" or higher | |
Regex for 5 or higher is "[5-9]" | |
Because remaining count of digits in sequence "3945" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "79\.0\.39[5-9]\d{1,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.* | |
Because digit 4 in sequence "3945" is "5", roll it to "6" or higher | |
Regex for 6 or higher is "[6-9]" | |
Wildcard everything else | |
Complete regex is "79\.0\.394[6-9].*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].* | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\. | |
Evaluating sequence 4 of 4 | |
Sequence 4 is "117" | |
Count of digits in sequence "117" is 3 | |
Count of digits in sequence "117" may roll over to 4 or more digits | |
Regex for 4 or more digits is "79\.0\.3945\.\d{4,}" | |
Wildcard everything else | |
Complete regex is "79\.0\.3945\.\d{4,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.* | |
Because digit 1 in sequence "117" is "1", roll it to "2" or higher | |
Regex for 2 or higher is "[2-9]" | |
Because remaining count of digits in sequence "117" is 2, pad the sequence with 2 more digit(s) | |
Regex for 2 more digit(s) is \d{2,} | |
Wildcard everything else | |
Complete regex is "79\.0\.3945\.[2-9]\d{2,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.* | |
Because digit 2 in sequence "117" is "1", roll it to "2" or higher | |
Regex for 2 or higher is "[2-9]" | |
Because remaining count of digits in sequence "117" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "79\.0\.3945\.1[2-9]\d{1,}.*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.*|79\.0\.3945\.1[2-9]\d{1,}.* | |
Because digit 3 in sequence "117" is "7", roll it to "8" or higher | |
Regex for 8 or higher is "[8-9]" | |
Wildcard everything else | |
Complete regex is "79\.0\.3945\.11[8-9].*" | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.*|79\.0\.3945\.1[2-9]\d{1,}.*|79\.0\.3945\.11[8-9].* | |
Progressive regex: \d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.*|79\.0\.3945\.1[2-9]\d{1,}.*|79\.0\.3945\.11[8-9].*|79\.0\.3945\.117 | |
Adding original version string to end of regex as a potential match. | |
Regex for "79.0.3945.117" or higher (249 characters): | |
^(\d{3,}.*|[8-9]\d{1,}.*|79\.\d{2,}.*|79\.[1-9].*|79\.0\.\d{5,}.*|79\.0\.[4-9]\d{3,}.*|79\.0\.39[5-9]\d{1,}.*|79\.0\.394[6-9].*|79\.0\.3945\.\d{4,}.*|79\.0\.3945\.[2-9]\d{2,}.*|79\.0\.3945\.1[2-9]\d{1,}.*|79\.0\.3945\.11[8-9].*|79\.0\.3945\.117.*)$ |
Version string is 5.0.3-24978.0517 (4323) | |
Adjusted version string for parsing is "5.0.3.24978.0517.4323" | |
Number of sequences is 6 | |
Replacing digits in sequences to get the sequence dividers "###.###.###-###.### (###)" | |
Evaluating sequence 1 of 6 | |
Sequence 1 is "5" | |
Count of digits in sequence "5" is 1 | |
Count of digits in sequence "5" may roll over to 2 or more digits | |
Regex for 2 or more digits is "\d{2,}" | |
Wildcard everything else | |
Complete regex is "\d{2,}.*" | |
Progressive regex: \d{2,}.* | |
Because digit 1 in sequence "5" is "5", roll it to "6" or higher | |
Regex for 6 or higher is "[6-9]" | |
Wildcard everything else | |
Complete regex is "[6-9].*" | |
Progressive regex: \d{2,}.*|[6-9].* | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{2,}.*|[6-9].*|5\. | |
Evaluating sequence 2 of 6 | |
Sequence 2 is "0" | |
Count of digits in sequence "0" is 1 | |
Count of digits in sequence "0" may roll over to 2 or more digits | |
Regex for 2 or more digits is "5\.\d{2,}" | |
Wildcard everything else | |
Complete regex is "5\.\d{2,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.* | |
Because digit 1 in sequence "0" is "0", roll it to "1" or higher | |
Regex for 1 or higher is "[1-9]" | |
Wildcard everything else | |
Complete regex is "5\.[1-9].*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].* | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\. | |
Evaluating sequence 3 of 6 | |
Sequence 3 is "3" | |
Count of digits in sequence "3" is 1 | |
Count of digits in sequence "3" may roll over to 2 or more digits | |
Regex for 2 or more digits is "5\.0\.\d{2,}" | |
Wildcard everything else | |
Complete regex is "5\.0\.\d{2,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.* | |
Because digit 1 in sequence "3" is "3", roll it to "4" or higher | |
Regex for 4 or higher is "[4-9]" | |
Wildcard everything else | |
Complete regex is "5\.0\.[4-9].*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].* | |
Next character is "-" | |
This character does not need escaping | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3- | |
Evaluating sequence 4 of 6 | |
Sequence 4 is "24978" | |
Count of digits in sequence "24978" is 5 | |
Count of digits in sequence "24978" may roll over to 6 or more digits | |
Regex for 6 or more digits is "5\.0\.3-\d{6,}" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-\d{6,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.* | |
Because digit 1 in sequence "24978" is "2", roll it to "3" or higher | |
Regex for 3 or higher is "[3-9]" | |
Because remaining count of digits in sequence "24978" is 4, pad the sequence with 4 more digit(s) | |
Regex for 4 more digit(s) is \d{4,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-[3-9]\d{4,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.* | |
Because digit 2 in sequence "24978" is "4", roll it to "5" or higher | |
Regex for 5 or higher is "[5-9]" | |
Because remaining count of digits in sequence "24978" is 3, pad the sequence with 3 more digit(s) | |
Regex for 3 more digit(s) is \d{3,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-2[5-9]\d{3,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.* | |
Because "Digit 3" in sequence "24978" is 9, do nothing | |
Because digit 4 in sequence "24978" is "7", roll it to "8" or higher | |
Regex for 8 or higher is "[8-9]" | |
Because remaining count of digits in sequence "24978" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-249[8-9]\d{1,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.* | |
Because digit 5 in sequence "24978" is "8", roll it to "9" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24979.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.* | |
Next character is "." | |
Escaping "." to create "\." | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\. | |
Evaluating sequence 5 of 6 | |
Sequence 5 is "0517" | |
Count of digits in sequence "0517" is 4 | |
Count of digits in sequence "0517" may roll over to 5 or more digits | |
Regex for 5 or more digits is "5\.0\.3-24978\.\d{5,}" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.\d{5,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.* | |
Because digit 1 in sequence "0517" is "0", roll it to "1" or higher | |
Regex for 1 or higher is "[1-9]" | |
Because remaining count of digits in sequence "0517" is 3, pad the sequence with 3 more digit(s) | |
Regex for 3 more digit(s) is \d{3,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.[1-9]\d{3,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.* | |
Because digit 2 in sequence "0517" is "5", roll it to "6" or higher | |
Regex for 6 or higher is "[6-9]" | |
Because remaining count of digits in sequence "0517" is 2, pad the sequence with 2 more digit(s) | |
Regex for 2 more digit(s) is \d{2,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0[6-9]\d{2,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.* | |
Because digit 3 in sequence "0517" is "1", roll it to "2" or higher | |
Regex for 2 or higher is "[2-9]" | |
Because remaining count of digits in sequence "0517" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.05[2-9]\d{1,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.* | |
Because digit 4 in sequence "0517" is "7", roll it to "8" or higher | |
Regex for 8 or higher is "[8-9]" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.051[8-9].*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].* | |
Next character is " " | |
This character does not need escaping | |
Next character is "(" | |
Escaping "(" to create "\(" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \( | |
Evaluating sequence 6 of 6 | |
Sequence 6 is "4323" | |
Count of digits in sequence "4323" is 4 | |
Count of digits in sequence "4323" may roll over to 5 or more digits | |
Regex for 5 or more digits is "5\.0\.3-24978\.0517 \(\d{5,}" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0517 \(\d{5,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.* | |
Because digit 1 in sequence "4323" is "4", roll it to "5" or higher | |
Regex for 5 or higher is "[5-9]" | |
Because remaining count of digits in sequence "4323" is 3, pad the sequence with 3 more digit(s) | |
Regex for 3 more digit(s) is \d{3,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0517 \([5-9]\d{3,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.* | |
Because digit 2 in sequence "4323" is "3", roll it to "4" or higher | |
Regex for 4 or higher is "[4-9]" | |
Because remaining count of digits in sequence "4323" is 2, pad the sequence with 2 more digit(s) | |
Regex for 2 more digit(s) is \d{2,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.* | |
Because digit 3 in sequence "4323" is "2", roll it to "3" or higher | |
Regex for 3 or higher is "[3-9]" | |
Because remaining count of digits in sequence "4323" is 1, pad the sequence with 1 more digit(s) | |
Regex for 1 more digit(s) is \d{1,} | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*|5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.* | |
Because digit 4 in sequence "4323" is "3", roll it to "4" or higher | |
Regex for 4 or higher is "[4-9]" | |
Wildcard everything else | |
Complete regex is "5\.0\.3-24978\.0517 \(432[4-9].*" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*|5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.*|5\.0\.3-24978\.0517 \(432[4-9].* | |
Next character is ")" | |
Escaping ")" to create "\)" | |
Progressive regex: \d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*|5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.*|5\.0\.3-24978\.0517 \(432[4-9].*|5\.0\.3-24978\.0517 \(4323\) | |
Adding original version string to end of regex as a potential match. | |
=============================================== | |
WARNING | |
This version string contains non-standard | |
characters or number sequences that begin | |
with a zero (i.e. "0123", which is the | |
same as "123"). | |
Use regexes with caution. | |
=============================================== | |
Regex for "5.0.3-24978.0517 (4323)" or higher (522 characters): | |
^(\d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*|5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*|5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*|5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.*|5\.0\.3-24978\.0517 \(432[4-9].*|5\.0\.3-24978\.0517 \(4323\).*)$ | |
Jamf Pro has a field character limit of 255 characters. | |
This regex exceeds that field character limit. | |
Add additional "Application Version" criteria to your search | |
and paste each regex string into the the additional fields. | |
For example: | |
Application Title is Google Chrome.app | |
and ( Application Version matches regex <Regex 1> | |
or Application Version matches regex <Regex 2> ) | |
Regex 1: | |
^(\d{2,}.*|[6-9].*|5\.\d{2,}.*|5\.[1-9].*|5\.0\.\d{2,}.*|5\.0\.[4-9].*|5\.0\.3-\d{6,}.*)$ | |
Regex 2: | |
^(5\.0\.3-[3-9]\d{4,}.*|5\.0\.3-2[5-9]\d{3,}.*|5\.0\.3-249[8-9]\d{1,}.*|5\.0\.3-24979.*|5\.0\.3-24978\.\d{5,}.*|5\.0\.3-24978\.[1-9]\d{3,}.*|5\.0\.3-24978\.0[6-9]\d{2,}.*|5\.0\.3-24978\.05[2-9]\d{1,}.*)$ | |
Regex 3: | |
^(5\.0\.3-24978\.051[8-9].*|5\.0\.3-24978\.0517 \(\d{5,}.*|5\.0\.3-24978\.0517 \([5-9]\d{3,}.*|5\.0\.3-24978\.0517 \(4[4-9]\d{2,}.*|5\.0\.3-24978\.0517 \(43[3-9]\d{1,}.*|5\.0\.3-24978\.0517 \(432[4-9].*|5\.0\.3-24978\.0517 \(4323\).*)$ |
@grahampugh I asked Jamf support for a status on PI-008150, and they replied that they're still waiting for a response from engineering. Since it was reported 2 months ago you might try adding your voice as being impacted to see if generates any extra traction.
Hi @talkingmoose, a colleague has come across an edge case where the following line (312) is being scuppered by the presence of an actual b
in the version string:
dividedRegex=$( /usr/bin/sed "s/|/b/$breakDelimiterPosition" <<< "$dividedRegex" )
I'm curious as to (1) why the |
needs to be substituted at all, and (2) if there is a specific reason for choosing b
as the replacement string that I'm not seeing? I'm thinking of replacing it with a little-used character such as §
or ±
.
Hi @grahampugh! It's been a while since I've revisited this gist, but I believe the sed substitution was a response to having to break long regex strings into smaller strings when used with Jamf Pro. Its fields had a limit of around 250 characters, and long version strings like Google's needed a solution. I probably used "b" for "break here at this 'or' statement".
You're right that most any character (or short string) here would work. You'll also need to adjust the awk field delimiter on line 334 to use the same character or short string.
In the interest of not having to deal with breaking a >255 character pattern into smaller versions I've adopted Cameron Moore's make_ge_version_regexp.sh fork of this script (https://github.com/moorereason/make_ge_version_regex) which generates much shorter patterns. While the README for it refers to the optimized patterns generated as being Jamf-unsafe I have not run into an instance where they haven't worked.
@talkingmoose thanks, I've gone ahead and switched out the b
for a ±
in my version of the script and it seems to be doing the trick.
% ./match-version-number-or-higher.bash -j -q 1.2.35.663.gb699649e
^(\d{2,}.*|[2-9].*|1\.\d{2,}.*|1\.[3-9].*|1\.2\.\d{3,}.*|1\.2\.[4-9]\d{1,}.*|1\.2\.3[6-9].*|1\.2\.35\.\d{4,}.*|1\.2\.35\.[7-9]\d{2,}.*|1\.2\.35\.6[7-9]\d{1,}.*|1\.2\.35\.66[4-9].*)$
^(1\.2\.35\.663\.gb\d{7,}.*|1\.2\.35\.663\.gb[7-9]\d{5,}.*|1\.2\.35\.663\.gb699[7-9]\d{2,}.*|1\.2\.35\.663\.gb6996[5-9]\d{1,}.*|1\.2\.35\.663\.gb699649e.*)$
@sdagley thanks for that, I wasn't aware of that version.
I'm no longer maintaining a Jamf instance, so feel free to take my optimizations and run with them.
P.S. - My version generates a 167 character pattern for that version string versus the 291 character pattern from Will's script. 😉 However, I will say I'm super-impressed by Will's script and thoroughly enjoyed dissecting it to understand how it worked and how I might could improve it. 👏
P.P.S. - I forgot about this whole project and didn't know if anyone even knew it was out there, so thanks for making my day, @sdagley!
@grahampugh I opened PI-008150 for the 255 character regex limit, but I just checked the Jamf Pro Known Issues page and it's not listed (nor does searching on "regex" have any hits). Hopefully @talkingmoose can determine the status of that PI.