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
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
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
# 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:
Below are some examples I tried for various types of rpms
libssh2-1.4.13-4.el7_1.20.x86_64 < libssh2-1.4.13-13.el7_1.20.x86_64
selinux-policy-targeted-3.13.1-166.el7.noarch > selinux-policy-targeted-3.12.1-166.el7.noarch
plymouth-core-libs-0.8.9-0.28.20140113.el7.x86_64 < plymouth-core-libs-0.8.10-0.28.20140114.el7.x86_64
net-tools-2.0-0.22.20131004git.el7.x86_64 < net-tools-2.0-0.22.20141004git.el7.x86_64
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
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.