Skip to content

Instantly share code, notes, and snippets.

@caruccio
Last active March 18, 2024 16:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caruccio/804d53d19c7c993aba31dbd3da4c9d68 to your computer and use it in GitHub Desktop.
Save caruccio/804d53d19c7c993aba31dbd3da4c9d68 to your computer and use it in GitHub Desktop.
Print all latest patch-level version

How to get the latest patch-level version from a list of distinct versions using bash and awk. This idea was taken from https://stackoverflow.com/a/21103864/1006369

Supose you have the following list of versions:

v1.22.8
v1.22.9
v1.22.10   <-- latest v1.22 series
v1.23.1
v1.23.2    <-- latest v1.23 series
v1.24.1
v1.24.2
v1.24.3
v1.24.4    <-- latest v1.24 series

And you need to extract the latest PATCH of each version series. The result thus must be this:

v1.22.10
v1.23.2
v1.24.4

Using AWK's pattern expressions it's possible to evaluate only the first element of a associative-array, negate the eval and print (by default):

First, create the file with the versions. It doesn't matter if it's sorted or not yet.

$ cat >versions.txt <<EOF
v1.22.8
v1.22.9
v1.22.10
v1.23.1
v1.23.2
v1.24.1
v1.24.2
v1.24.3
v1.24.4
EOF

Use sort's flags -V to sort by version number and -r to reverse the sorted values, from newest to oldest:

sort -Vr versions.txt
v1.24.4
v1.24.3
v1.24.2
v1.24.1
v1.23.2
v1.23.1
v1.22.10
v1.22.9
v1.22.8

Sending this output to awk gives the latest version of each series:

$ sort -Vr versions.txt | awk -F . '!a[$1 FS $2]++'
v1.24.4
v1.23.2
v1.22.10

Let's analyse what awk does:

  • -F .: Uses . as the field separator. Each line will be splited by ., creating 3 fields: $1=v1 (3 times), $2=24|23|22 and $3=4|2|10.
  • a[$1 FS $2]: Creates an associative array a at position $1 FS $2 (the values are concatenated), where $1=v1 and $2=24 (ex, at the first input line), and FS if the field-separator (.). Since this posistion doesn't exists, awk initializes it with null.
  • a[$1 FS $2]++: Postfix-increments the value from 0 (null) to 1, turning it into a true value. The value 0 is used for the next instruction ! and 1 is stored at a[$1 FS $2].
  • !a[$1 FS $2]++: Negates the value 0 (false) to 1 (true), accepting the expression and printing the line.

This is just an expression to accept code blocks to execute. When the code block is not defined, like this one, the default {print} is executed.

At the next line for the same $1.$2 index, the value will be 1, thus negating it (!) will change it to 0, which evaluates to false, avoinding the default {print} code block. Any other match for $1.$2 will produce a false evaluation.

Note this only works if the input lines are sorted with the newest patch first, since awk will basically filter for the first item it finds.

If you need to have the list sorted from oldest to newest, just use sort -V again:

$ sort -Vr versions.txt | awk -F . '!a[$1 FS $2]++' | sort -V
v1.22.10
v1.23.2
v1.24.4

Genius!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment