(almost) Native BASH semver CHECK

Recently I needed a simple way to compare semver strings in a BASH script and was unable to find a solution that I liked so I wrote one. Why almost-native BASH? Leveraging the common available sort utility avoids re-implementing the version sort logic, reduces potential bugs and size of the function to a simple 3 lines vs 30+ lines for most BASH-native solutions.

This is working well in environments I manage. If you notice any bugs or issues please comment.

Function returns a standard -1, 0, 1 response. Basic unit tests included to validate functionality and provide example usage.

Enjoy!

#!/usr/bin/env bash

function semver_check {
  # sort low->high then pick the last one (highest)
  local HV; HV=$(echo -e "$1\n$2" |sort -V |tail -1)
  # They're not the same and $1 is not the high version = -1
  [[ "$1" != "$2" && "$1" != "$HV" ]] && echo -1 && return
  # 0 = they're the same; 1 = not the same
  [[ "$1" == "$2" ]]; echo $?
}

#
# TESTS
#

LV="1.2.3"
HV="2.3.4"

ARGS=("Test A: Ver1 == Ver2" "$LV" "$LV")
echo -n "${ARGS[*]} "
[[ $(semver_check "${ARGS[1]}" "${ARGS[2]}") -eq 0 ]] && echo "PASS" || echo "FAIL"

ARGS=("Test B: Ver1 < Ver2" "$LV" "$HV")
echo -n "${ARGS[*]} "
[[ $(semver_check "${ARGS[1]}" "${ARGS[2]}") -lt 0 ]] && echo "PASS" || echo "FAIL"

ARGS=("Test C: Ver1 > Ver2" "$HV" "$LV")
echo -n "${ARGS[*]} "
[[ $(semver_check "${ARGS[1]}" "${ARGS[2]}") -gt 0 ]] && echo "PASS" || echo "FAIL"

ARGS=("Test D: Ver1 >= Ver2" "$HV" "$LV")
echo -n "${ARGS[*]} "
RESULT="PASS"
[[ $(semver_check "${ARGS[1]}" "${ARGS[2]}") -ge 0 ]] || RESULT="FAIL"
[[ $(semver_check "${ARGS[1]}" "${ARGS[1]}") -ge 0 ]] || RESULT="FAIL"
echo ${RESULT}

ARGS=("Test E: Ver1 <= Ver2" "$LV" "$HV")
echo -n "${ARGS[*]} "
RESULT="PASS"
[[ $(semver_check "${ARGS[1]}" "${ARGS[2]}") -le 0 ]] || RESULT="FAIL"
[[ $(semver_check "${ARGS[1]}" "${ARGS[1]}") -le 0 ]] || RESULT="FAIL"
echo ${RESULT}

Addendum: The sort binary dependency must support natural version sort option (-V, --version-sort) This option is available on GNU coreutils 7+ and is also available on Mac OS 10.14+

Inline parsing JSON objects in 3 languages

JavaScript Object Notation commonly known as JSON, is a convenient format for many reasons and it is no wonder that many APIs and web services today support returning (in some cases exclusively) JSON.  Many times I have had a need to parse values from large JSON Objects in a shell script environment or a command line shell, and use them as inputs to another program.  This has put me on the look out for easy ways to handle inline parsing of JSON Objects using languages commonly available on modern Linux systems.

Below are examples in three languages (Node JS, Python and Perl) to accomplish this task.

Example JSON Object:

{"product": {"builds": {"1234": "IT WORKS!"}, "default": "1234"}}

Inline Node JS:

node -e "s=''; i=process.stdin; i.on('data', function(d) { s += d }); i.on('end', function() { j=JSON.parse(s).product; console.log(j.builds[j.default]) })"

Inline Python:

python -c 'import sys, json; j=json.load(sys.stdin)["product"]; print j["builds"][j["default"]]'

Inline Perl:

perl -e 'use JSON; local $/; $d=decode_json(<>)->{product}; print $d->{builds}->{$d->{default}}'
  • The JSON module is required in the above example. If not already available it can easily be installed via CPAN with:
sudo perl -MCPAN -e 'install JSON'


Full example for Python:

echo '{"product": {"builds": {"1234": "IT WORKS!"}, "default": "1234"}}' |python -c 'import sys, json; j=json.load(sys.stdin)["product"]; print j["builds"][j["default"]]'