Home > Enterprise >  Version sorting strings with numbers in bash returns incorrect result
Version sorting strings with numbers in bash returns incorrect result

Time:03-25

I need the latest version out of these 4 kernel versions.

  • 4.18.0-187.el8.x86_64
  • 4.18.0-193.14.3.el8_2.x86_64
  • 4.18.0-193.el8.x86_64
  • 4.18.0-80.el8.x86_64

I had initially used numeric sort (which returns the 0-80 version incorrectly) before moving onto version sort for the same

latest_kernel_in_use=$(ls boot/vmlinuz* | sed 's/\/boot\/vmlinuz-//' | sort -V | tail -n1 )

The command still returns 4.18.0-193.el8.x86_64 versus the desired 4.18.0-193.14.3.el8_2.x86_64 output.

Help me out with the correction in the command. I tested additionally and it's really the suffixes .el8.x86_64 complicating the sorting.

CodePudding user response:

You'll have to strip those suffixes for sort -V to work as expected.

Here's a workaround with a decorate | sort | undecorate pattern:

#!/bin/bash

latest_kernel_installed=$(
     compgen -G '/boot/vmlinuz-*' |
     sed -nE 's/^(.*\/)?[^/-]*-(([^/]*)(\.[^./]*){2})$/\3 \2/p' |
     sort -r -V -k1,1 |
     awk 'NR==1{print $2; exit}' 
)

notes:

  • I'm using bash compgen -G instead of ls because it's a lot faster

  • In the sed expression, I'm filtering out the files that don't match the pattern (like for example /boot/vmlinuz-0-rescue-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa), prepending the decoration (extracted version number) and removing /boot/vmlinuz- from the output.

  • Then I sort -V the decoration and reverse the output to have the latest version at the top

  • Then I remove the decoration


That said, listing /boot/vmlinuz-* will just give you an idea of the installed kernels. If you want to know the kernel version that is currently running then you can just do:

uname -r

CodePudding user response:

seems to be because "e" > 1

I came up with this horror but I am sure there is a better way

#!/bin/bash

# list your kernels and remove "vmlinuz-" prefix
KERNELS=$(find boot/ -iname "vmlinuz*" -exec basename {} \; | sed s/vmlinuz-//g)

# declare empty arrays (this will be arrays to contain arrays)
VERSIONS_ARRAY=()
SUFFIXES_ARRAY=()


for KERNEL in $KERNELS;do
  # we will split each part of the kernel version on the dot (".")
  IFS="."
  read -ra KERNEL_VERSION <<< "$(echo $KERNEL)"
  # we define 2 empty strings to collect the kernel parts
  VERSION_STRING=""
  SUFFIX_STRING=""
  # we set back field separator to space
  IFS=" "
  # we loop throug all parts of kernel version
  for KERNEL_PART in ${KERNEL_VERSION[@]};do
    #echo -n $KERNEL_PART
    # if we encounter el8 or el8_2 or x86_64 string we append to the SUFFIX_STRING
    if [[ "$KERNEL_PART" == "el8" ]] || [[ "$KERNEL_PART" == "el8_2" ]] || [[ "$KERNEL_PART" == "x86_64" ]] ;then
      SUFFIX_STRING="${SUFFIX_STRING}${KERNEL_PART}."
    # else, we append to the VERSION_STRING
    else
      VERSION_STRING="${VERSION_STRING}${KERNEL_PART}."
    fi   
  done
  # we append the VERSION_STRING in VERSION_STRINGS
  VERSIONS_ARRAY =("$VERSION_STRING")
  # we append the SUFFIX_STRING in SUFFIXES_ARRAYS
  SUFFIXES_ARRAY =("$SUFFIX_STRING") 
done

# find the highest value in version array
IFS=$'\n'
HIGHEST_VERSION=$(echo "${VERSIONS_ARRAY[*]}" | sort -V | tail -1)

# get position of highest value in array
for INDEX in ${VERSIONS_ARRAY[@]};do
    if [[ "$VERSIONS_ARRAY{[$INDEX]}" = "$HIGHEST_VERSION" ]]; then
        INDEX_OF_HIGHER_VERSION=$INDEX
    fi
done

# get the corresponding suffix by index
HIGHEST_VERSION_SUFFIX=${SUFFIXES_ARRAY[$INDEX_OF_HIGHER_VERSION]}

#remove the trailing dot in suffix
len=${#HIGHEST_VERSION_SUFFIX}
HIGHEST_VERSION_SUFFIX=${HIGHEST_VERSION_SUFFIX::len-1}

echo "vmlinuz-${HIGHEST_VERSION}${HIGHEST_VERSION_SUFFIX}"
  • Related