Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Generates a regular expression (regex) that matches the provided version number or higher. Useful for Jamf Pro's "matches regex" operator in searches and smart groups where the results need to be the current version of an app or higher.
#!/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\).*)$
@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Apr 16, 2020

@talkingmoose

Thank you! This is incredibly useful. It appears to be capable of generating a RegEx that Jamf Pro can't handle however, at least version 10.19.0...

The new version of Microsoft AutoUpdate is 4.22.20041300, and the RegEx this script generates to match that version or higher is ^(\d{2,}.|[5-9].|4.\d{3,}.|4.[3-9]\d{1,}.|4.2[3-9].|4.22.\d{9,}.|4.22.[3-9]\d{7,}.|4.22.2[1-9]\d{6,}.|4.22.20[1-9]\d{5,}.|4.22.200[5-9]\d{4,}.|4.22.2004[2-9]\d{3,}.|4.22.20041[4-9]\d{2,}.|4.22.200413[1-9]\d{1,}.|4.22.2004130[1-9].|4.22.20041300.*)$

That expression does correctly evaluate version number 4.22.20041300 and higher when tested on https://regex101.com, but when attempting to create a Smart Group with it used in a "matches regex" criteria you get an "An error occurred while saving the changes
See JAMFSoftwareServer.log for more details" attempting to save it. Here's the log output:

2020-04-15 23:03:50,486 [WARN ] [hread-49353] [CRUDHelper               ] - Update failed for object: Smart Computer Group
2020-04-15 23:03:55,261 [ERROR] [hread-49353] [ComputerGroupHelper      ] - java.sql.BatchUpdateException: (conn=172693) Data too long for column 'criteria' at row 1
java.sql.BatchUpdateException: (conn=172693) Data too long for column 'criteria' at row 1
	at org.mariadb.jdbc.MariaDbStatement.executeBatchExceptionEpilogue(MariaDbStatement.java:298)
	at org.mariadb.jdbc.ServerSidePreparedStatement.executeBatchInternal(ServerSidePreparedStatement.java:313)
	at org.mariadb.jdbc.ServerSidePreparedStatement.executeBatch(ServerSidePreparedStatement.java:215)
	at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:128)
	at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeBatch(HikariProxyPreparedStatement.java)
	at com.jamfsoftware.datasource.core.database.ThreeByteUtf8CompatiblePreparedStatement.executeBatch(ThreeByteUtf8CompatiblePreparedStatement.java:305)
	at com.jamfsoftware.datasource.util.database.DataSource.executeBatch(DataSource.java:817)
	at com.jamfsoftware.datasource.util.database.DataSource.executeBatch(DataSource.java:804)
	at com.jamfsoftware.jss.objects.computergroup.ComputerGroupHelper.update(ComputerGroupHelper.java:204)
	at com.jamfsoftware.jss.objects.computergroup.ComputerGroupHelper.update(ComputerGroupHelper.java:53)
	at com.jamfsoftware.jss.objects.CRUDHelper.update(CRUDHelper.java:254)
	at com.jamfsoftware.jss.objects.CRUDHelper.update(CRUDHelper.java:160)
	at com.jamfsoftware.jss.objects.CRUDHelper.update(CRUDHelper.java:141)
	at com.jamfsoftware.jss.objects.computergroup.ComputerGroup.update(ComputerGroup.java:191)
	at com.jamfsoftware.jss.frontend.HTMLResponse.save(HTMLResponse.java:1707)
	at com.jamfsoftware.jss.frontend.HTMLResponse.getProcessUpdateResponseCode(HTMLResponse.java:1661)
	at com.jamfsoftware.jss.frontend.HTMLResponse.performSave(HTMLResponse.java:1614)
	at com.jamfsoftware.jss.frontend.HTMLResponse.process(HTMLResponse.java:739)
	at com.jamfsoftware.jss.frontend.HTMLController.processRequest(HTMLController.java:144)
	at com.jamfsoftware.jss.frontend.HTMLController.doPost(HTMLController.java:76)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.jamfsoftware.jss.sso.filter.SsoFilter.doFilter(SsoFilter.java:66)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.jamfsoftware.jss.frontend.JSSAccessFilter.doFilter(JSSAccessFilter.java:71)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.jamfsoftware.jss.frontend.JSSLoadingFilter.doFilter(JSSLoadingFilter.java:182)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:109)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:609)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:810)
	at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.doRun(Nio2Endpoint.java:1630)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.net.AbstractEndpoint.processSocket(AbstractEndpoint.java:1082)
	at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$2.completed(Nio2Endpoint.java:566)
	at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$2.completed(Nio2Endpoint.java:544)
	at org.apache.tomcat.util.net.SecureNio2Channel$1.completed(SecureNio2Channel.java:969)
	at org.apache.tomcat.util.net.SecureNio2Channel$1.completed(SecureNio2Channel.java:898)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.Invoker$2.run(Invoker.java:219)
	at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.sql.SQLDataException: (conn=172693) Data too long for column 'criteria' at row 1
	at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.get(ExceptionMapper.java:231)
	at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.getException(ExceptionMapper.java:171)
	at org.mariadb.jdbc.MariaDbStatement.executeBatchExceptionEpilogue(MariaDbStatement.java:295)
	... 61 more
Caused by: java.sql.SQLException: Data too long for column 'criteria' at row 1
	at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readErrorPacket(AbstractQueryProtocol.java:1594)
	at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readPacket(AbstractQueryProtocol.java:1453)
	at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.getResult(AbstractQueryProtocol.java:1415)
	at org.mariadb.jdbc.internal.protocol.AsyncMultiRead.call(AsyncMultiRead.java:142)
	at org.mariadb.jdbc.internal.protocol.AsyncMultiRead.call(AsyncMultiRead.java:68)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	... 3 more
2020-04-15 23:03:55,263 [WARN ] [hread-49353] [CRUDHelper               ] - Update failed for object: Smart Computer Group
@talkingmoose

This comment has been minimized.

Copy link
Owner Author

@talkingmoose talkingmoose commented Apr 16, 2020

Thanks, @sdagley! I'm able to reproduce this.

It appears the value field in Jamf Pro has a character limit of 255. I'll open a ticket with Jamf to see if this can be expanded. Depending on their code, it may not be possible, but I can ask.

The longest version string I tested was for Adobe Acrobat Reader. The version for Classic Track is "17.011.30166" and contains 10 digits, which currently returns a regex string with a length of 251 characters. This Microsoft AutoUpdate string is 11 digits.

These are three things you can do in the meantime in the order I'd recommend:

  1. Limit the version string to Microsoft AutoUpdate's CFBundleShortVersionString (the same version you'd see in the Finder's Get Info window), which is currently "4.22". I think this is the easiest and best solution.
  2. For MAU specifically, you can truncate the last two digits, which are 00. The third sequence is just a date in reverse format. Microsoft is highly unlikely to release a 01 or higher. For me, this generates a regex string with a length of 226 characters.
  3. Truncate the first three regexes at the beginning of the string the script generates ( \d{2,}.*|[5-9].*|4\.\d{3,}.* ). The most important matches are arguably those to the right because those match closer and closer to the actual version string you provided. Those further to the left match much higher numbers. For example, \d{2,}.* is only useful when the first digit of MAU's version goes to two or more digits. It's only at 4 right now and that may never happen. The second regex [5-9].* accounts for any major version after 4.

I'll also look at adding a warning about the regex length, so that folks aren't surprised, and I'll include these suggestions as part of the warning.

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Apr 16, 2020

@talkingmoose, thanks for the quick response! I will also open a support ticket to see if this will turn into a PI.

Normally I'd trust CFBundleShortVersionString to be correct, but went with workaround #2 given my current hate/hate relationship with MAU. Of all the MS apps related to Office it is the one I expect would most likely need an out-of-band update which somehow would be done without changing the short version.

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Apr 20, 2020

@talkingmoose Jamf Pro PI-008150 has been created for the problem with a regex > 255 characters. I haven't seen the actual PI yet, so I'm not sure if it's specific to a regex, or generic to the limit of 255 characters in the value field of a Smart Group.

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Apr 21, 2020

Left without further comment... Microsoft has now released two different versions of Microsoft Auto Update in the past week. - 4.22.20041300 and 4.22.20042003. Both report 4.22 as their CFBundleShortVersionString.

@talkingmoose

This comment has been minimized.

Copy link
Owner Author

@talkingmoose talkingmoose commented May 6, 2020

@sdagley, letting you know I've modified the script to break longer regex strings into two shorter strings for Jamf Pro. If the character length of the regex exceeds 255, the script will display this:

Regex for "81.0.4044.138" or higher (269 characters):

^(\d{3,}.*|9\d{1,}.*|8[2-9].*|81\.\d{2,}.*|81\.[1-9].*|81\.0\.\d{5,}.*|81\.0\.[5-9]\d{3,}.*|81\.0\.4[1-9]\d{2,}.*|81\.0\.40[5-9]\d{1,}.*|81\.0\.404[5-9].*|81\.0\.4044\.\d{4,}.*|81\.0\.4044\.[2-9]\d{2,}.*|81\.0\.4044\.1[4-9]\d{1,}.*|81\.0\.4044\.139.*|81.0.4044.138.*)$


Jamf Pro has a field character limit of 255 characters.
This regex exceeds that field character limit.
Add a second "Application Version" criterion to your search,
paste the first regex string into the first field and
paste the second regex string into the second field.


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{3,}.*|9\d{1,}.*|8[2-9].*|81\.\d{2,}.*|81\.[1-9].*|81\.0\.\d{5,}.*|81\.0\.[5-9]\d{3,}.*)$

Regex 2: ^(81\.0\.4[1-9]\d{2,}.*|81\.0\.40[5-9]\d{1,}.*|81\.0\.404[5-9].*|81\.0\.4044\.\d{4,}.*|81\.0\.4044\.[2-9]\d{2,}.*|81\.0\.4044\.1[4-9]\d{1,}.*|81\.0\.4044\.139.*|81.0.4044.138.*)$
@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented May 7, 2020

@talkingmoose Much appreciated. Your script inspired me to regex'ify many of my Smart Groups and eliminate ones that relied on nesting other Smart Groups to accommodate multiple versions.

@star-affinity

This comment has been minimized.

Copy link

@star-affinity star-affinity commented May 8, 2020

This is great – fantastic job! @talkingmoose

I have one concern though – how would I go forth if the version number for an application includes parentheses like for example the Zoom app which currently has version: 5.0.1 (23508.0430)
?

I haven't tried yet, but can imagine things will get confused if I include those parentheses into the Regex field in Jamf, or maybe not?

@talkingmoose

This comment has been minimized.

Copy link
Owner Author

@talkingmoose talkingmoose commented May 8, 2020

@star-affinity, right now, the script is intended for version strings with numbers and decimals only.

Oh, Zoom.

Zoom. Zoom. Zoom.

I have an idea in mind and it may be easy to implement. I'll respond here if I add something new.

In the meantime, I suggest you and I and anyone else who's interested reach out to Zoom (I'm thinking Twitter) and explain to them they don't need to put this in the CFBundleShortVersionString as 5.0.1 (23508.0430) when it's already in the CFBundleVersionString as 5.0.23508.0430. And point them to Semantic Versioning. Given their recent 180 about fixing security problems, maybe they'll be more inclined to listen to best practices too.

@talkingmoose

This comment has been minimized.

Copy link
Owner Author

@talkingmoose talkingmoose commented May 24, 2020

@star-affinity, try out the latests version of the script. I've added support for version strings with non-standard characters (i.e. more than just digits and decimals).

I've tested with a Zoom version string, which contains a space and parentheses. I've also tested a random version string with a dash, space and parentheses. (See the Sample Output 3 above.)

Be sure to put the version string in quotes if it contains a space.
"/Users/talkingmoose/Desktop/Desktop/Match Version Or Higher.bash" "5.0.3 (24978.0517)"

The script will always warn about non-standard version strings. If developers don't follow standards then there's nothing a script can do to standardize their version strings. But I've tried my best for now. Do continue, though, to push software developers toward supporting Semantic Versioning.

Let me know if you find any issues.

@star-affinity

This comment has been minimized.

Copy link

@star-affinity star-affinity commented Jun 2, 2020

@talkingmoose

Wow, thanks! :)
I'l try to give it spin today or later this week.
Will also tweet to Zoom about Semantic Versioning!

Edit:

it seems to work fine!
First I thought there was something wrong since my computer with Zoom 5.0.5 got listed in the Smart Group in Jamf despite the regex setting being to only list those not having 5.0.5 or higher. But that seemed to be due to the fact that Zoom as of late started to have different numbers in the Bundle version and the Bundle version string (short) field. In the zoom.us.app I currently have installed I have 5.0.26223.0603 in the Bundle version field and 5.0.5 (26223.0603) in the Bundle version string (short) field in the Info.plist file within zoom.us.app. No wonder Jamf gets confused. :)

I've tweeted Zoom about this too…

@grahampugh

This comment has been minimized.

Copy link

@grahampugh grahampugh commented Jun 12, 2020

Hi @talkingmoose, many thanks for this script. I've been thinking about how it could be used as part of an automation workflow, since the regex is only good for whatever the current version is, so needs to change every time there is an update.

I'd love to try and incorporate this into an AutoPkg. It would be simple enough to make a Processor that called the script via a subprocess command. It's the output of the current script that is problematic. Have you considered a parameter or option to output either just the regex without additional info strings, or output to JSON or a Plist so that the relevant value can be easily extracted?

@talkingmoose

This comment has been minimized.

Copy link
Owner Author

@talkingmoose talkingmoose commented Jun 12, 2020

@grahampugh, I can certainly include something like this for munki.

Let's think about the right approach. Right now, I have a couple of variables that require toggling within the script to make it behave differently. I can add another toggle for "regex only". Would it be wise to convert all of these to options that can be called as part of the command? Something like -J | --jamf-output, and -V | --verbose-output?

I'd then make "regex only " the default behavior without any options.

Or does it satisfy a need to keep these as variables in the script itself? I'm leaning toward the first idea.

@grahampugh

This comment has been minimized.

Copy link

@grahampugh grahampugh commented Jun 15, 2020

@talkingmoose something long those lines seems perfect. I don't mean to change the default behaviour that you envisaged (unless you want to!), but a flag such as -r | --regex-only or -q | quiet which outputted only the regex would make sense. I'm not sure if --jamf-output is an appropriate name, since the regex-only output could be used in any system.

I would personally either create a new AutoPkg processor to run the script and provide the regex as an output variable for subsequent processors (e.g. JSSImporter), or possibly incorporate the functionality directly into JSSImporter (I can do that, since I'm the maintainer) so that existing .jss recipes could be used. Only the SmartGroupTemplate.xml would need to be changed to incorporate the regex.

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Jun 15, 2020

@grahampugh Currently Jamf Pro cannot handle a regex expression longer than 255 characters, and some version strings result in a regex longer than that, so the Jamf mode was created to split the output in multiple shorter regexen

@grahampugh

This comment has been minimized.

Copy link

@grahampugh grahampugh commented Jun 15, 2020

@sdagley Ah, I see what you mean. That would be tricky (but essential) to somehow incorporate into the automation. Perhaps it could be done separately in the processor though. Or hopefully Jamf will lengthen the acceptable number of characters. @talkingmoose is that worth an FR?

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Jun 16, 2020

@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.

@sdagley

This comment has been minimized.

Copy link

@sdagley sdagley commented Jun 19, 2020

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.