How to check and compare rpm version and release in Linux using bash script

In my last article I had shared some samples script examples to compare decimal values and floating numbers by converting them into integer which can work well for some scenarios but would not be the best solution for this one.

Recently I came into a situation where I was supposed to install a hotfix patch on my Linux setup where I was supposed to programmatically compare the version of all the available rpms with the list of rpms downloaded from the yum repository. Now in most organization the linux servers would not be connected to internet hence the comparing part needs to be done manually.

Now I have an excel sheet which has 1000+ rpms and on my setup I have around 400+ rpms installed. Now I should compare rpms at both places and if any new rpm is found then update the same.

Comparing rpm versions is very tricky as each one has their own syntax and they do not follow a general syntax or naming convention other than the below format

name-release-version.arch

But here each section can be different.
With Red Hat we have a tool "rpmdev-vercmp" which is part of "yum-utils" rpm

This tool can be used to perform the comparison

# rpmdev-vercmp python-chardet-2.2.1-1.el7_1.noarch python-chardet-2.2.1-1.el7_2.noarch  
python-chardet-2.2.1-1.el7_1.noarch < python-chardet-2.2.1-1.el7_2.noarch

But my fade of luck, I am not allowed to install this additional rpm. So I decided to write my own algorithm to compare rpm versions and I came up with below script

#!/bin/bash

# Get both the package detail and store them in their respective variable  
pkg1="$1"  
pkg2="$2"

# Check if rpm is installed  
function check_rpm_status {  
 rpm -q $1 | grep -v "not installed" >/dev/null 2>&1  
 if [ $? == "0" ];then  
    return 0  
 else  
    return 1  
 fi  
}

function get_rpm_version {  
 python -c "print '-'.join('$1'.rsplit('-',2)[-2:])"  
}

# Get the version and release detail of both the packages  
if check_rpm_status $pkg1;then  
    pkg1_ver=`rpm -q --queryformat "%{VERSION}-%{RELEASE}.%{ARCH}n" $pkg1`  
else  
    pkg1_ver=$(get_rpm_version $pkg1)  
fi

if check_rpm_status $pkg2;then  
    pkg2_ver=`rpm -q --queryformat "%{VERSION}-%{RELEASE}.%{ARCH}n" $pkg2`  
else  
    pkg2_ver=$(get_rpm_version $pkg2)  
fi

# Modify the collected version and release detail to further compare them  
pkg1_ver_modified=`echo $pkg1_ver | sed -E -e 's/.el[0-9]|.x86_64|.i[0-9]86|.noarch//g; s/-|_/./g; s/[a-z]//g'`  
pkg2_ver_modified=`echo $pkg2_ver | sed -E -e 's/.el[0-9]|.x86_64|.i[0-9]86|.noarch//g; s/-|_/./g; s/[a-z]//g'`

# Store the version into array for comparison  
IFS="." read -a pkg1_array <<< ${pkg1_ver_modified}  
IFS="." read  -a pkg2_array<<< ${pkg2_ver_modified}

# Collect the result and store in these variables  
both_are_equal=0  
pkg1_is_greater=0  
pkg2_is_greater=0

# Main  
for ((i=0; i<${#pkg1_array[@]} || i<${#pkg2_array[@]}; i++)); do  
  [[ ${pkg1_array[$i]} -eq ${pkg2_array[$i]} ]] && ((both_are_equal++))  
  if [[ ${pkg1_array[$i]} -gt ${pkg2_array[$i]} ]];then  
     ((pkg1_is_greater++))  
     break  
  fi  
  [[ ${pkg1_array[$i]} -lt ${pkg2_array[$i]} ]] && ((pkg2_is_greater++))  
done

if [[ ${#pkg2_array[@]} -eq ${#pkg1_array[@]} && ${#pkg1_array[@]} -eq $both_are_equal ]];then  
   echo "$pkg1 is equal to $pkg2"  
elif [[ $pkg1_is_greater -gt "0" && $pkg2_is_greater -eq "0" ]];then  
   echo "$pkg1 > $pkg2"  
else  
   echo "$pkg1 < $pkg2"  
fi

This script will work same as "rpmdev-vercmp" tool.

Syntax:

/tmp/compare-rpm.sh $pkg1 $pkg2

Below are some examples I tried for various types of rpms

# ./compare-rpm.sh libssh2-1.4.13-4.el7_1.20.x86_64 libssh2-1.4.13-13.el7_1.20.x86_64  
libssh2-1.4.13-4.el7_1.20.x86_64 < libssh2-1.4.13-13.el7_1.20.x86_64

# ./compare-rpm.sh selinux-policy-targeted-3.13.1-166.el7.noarch selinux-policy-targeted-3.12.1-166.el7.noarch  
selinux-policy-targeted-3.13.1-166.el7.noarch > selinux-policy-targeted-3.12.1-166.el7.noarch

# ./compare-rpm.sh plymouth-core-libs-0.8.9-0.28.20140113.el7.x86_64 plymouth-core-libs-0.8.10-0.28.20140114.el7.x86_64  
plymouth-core-libs-0.8.9-0.28.20140113.el7.x86_64 < plymouth-core-libs-0.8.10-0.28.20140114.el7.x86_64

# ./compare-rpm.sh net-tools-2.0-0.22.20131004git.el7.x86_64 net-tools-2.0-0.22.20141004git.el7.x86_64  
net-tools-2.0-0.22.20131004git.el7.x86_64 < net-tools-2.0-0.22.20141004git.el7.x86_64

# ./compare-rpm.sh Red_Hat_Enterprise_Linux-Release_Notes-7-en-US-7-2.el7.noarch Red_Hat_Enterprise_Linux-Release_Notes-7-en-US-7-3.el7.noarch  
Red_Hat_Enterprise_Linux-Release_Notes-7-en-US-7-2.el7.noarch < Red_Hat_Enterprise_Linux-Release_Notes-7-en-US-7-3.el7.noarch

# ./compare-rpm.sh  linux-firmware-20170606-56.gitc990aae.el7.noarch linux-firmware-20170606-56.gitc998aae.el7.noarch  
linux-firmware-20170606-56.gitc990aae.el7.noarch < linux-firmware-20170606-56.gitc998aae.el7.noarch

Atleast this tool worked for me, I hope this can be useful to others reading this article.

Please share your feedback if you face any issues with this script, we can try to make this more robust.