Bash:将字符串(版本号)转换为整数

Bash:将字符串(版本号)转换为整数

我想获取 glibc 的版本,并在 if 语句中使用它,说明 glibc 是否小于 2.15。

然而问题是,当使用 ldd --version 时,它会作为字符串输出。我需要将其转换为整数才能进行小于比较。

以前我是这样做的

if [ "$(ldd --version | sed -n '1 p' | tr -cd '[:digit:]' | tail -c 3)" -lt 215 ]; then

这并不是很好,因为我必须删除小数点并且无法给出真实的版本号比较。另一个问题是某些版本的 glibc 有多个小数点(参见此处https://en.wikipedia.org/wiki/GNU_C_Library#Version_history)这意味着我现有的比较会混乱。

因此,为此,我需要能够将 glibc 版本完整地转换为整数,并正确比较版本号,即使有多个小数点。

有任何想法吗?谢谢

答案1

ldd --version | sed 's/.* //;q' | awk -F. '{ if ($1 > 2 || ($1 == 2 && $2 >= 15)) { exit 0 } else {exit 1} }'

解释:

sed命令获取 的第一行输出ldd --version,去掉最后一个空格之前的所有内容,然后退出(因此它只打印数字)。

设置为字段分隔符的-F标志。awk.

如果第一个数字(点之前)大于 2,或者如果第一个数字为 2 并且第二个数字至少为 15,则awk退出状态将为“true”。否则就是假的。

您可以在脚本中使用它,bash如下所示:

if ldd --version | sed 's/.* //;q' | awk -F. '{ if ($1 > 2 || ($1 == 2 && $2 >= 15)) { exit 0 } else {exit 1} }' ; then
  echo "Version is 2.15 or later"
else
  echo "Version is too old."
fi

答案2

sort可以对版本号进行排序;利用这一点,你可以写这样的东西:

if [ $(printf '%s\n2.15\n' $(ldd --version | sed -n '1s/.* //p') | sort -V | head -n 1) != 2.15 ]; then
    # ...
fi

printf '%s\n2.15\n' $(ldd --version | sed -n '1s/.* //p')打印ldd的版本号并2.15在两个单独的行中sort -V按升序对它们进行排序并head -n 1打印第一行;外部命令替换被输出替换,并将输出与2.15;如果输出不是2.15,则if执行主体。

我的机器上的示例输出ldd 2.21

% [ $(printf '%s\n2.15\n' $(ldd --version | sed -n '1s/.* //p') | sort -V | head -n 1) != 2.15 ] && printf 'Version %s is lower than 2.15\n' $(ldd --version | sed -n '1s/.* //p') || printf 'Version %s is equal or higher than 2.15\n' $(ldd --version | sed -n '1s/.* //p')
Version 2.21 is equal or higher than 2.15

一些硬编码值的示例输出,只是为了演示该方法处理版本排序的复杂性:

% glibc_version=2.15
% [ $(printf '%s\n2.15\n' $glibc_version | sort -V | head -n 1) != 2.15 ] && printf 'Version %s is lower than 2.15\n' $glibc_version || printf 'Version %s is equal or higher than 2.15\n' $glibc_version
Version 2.15 is equal or higher than 2.15
% glibc_version=2.16  
% [ $(printf '%s\n2.15\n' $glibc_version | sort -V | head -n 1) != 2.15 ] && printf 'Version %s is lower than 2.15\n' $glibc_version || printf 'Version %s is equal or higher than 2.15\n' $glibc_version
Version 2.16 is equal or higher than 2.15
% glibc_version=2.15.1
[ $(printf '%s\n2.15\n' $glibc_version | sort -V | head -n 1) != 2.15 ] && printf 'Version %s is lower than 2.15\n' $glibc_version || printf 'Version %s is equal or higher than 2.15\n' $glibc_version
Version 2.15.1 is equal or higher than 2.15
% glibc_version=2.14
% [ $(printf '%s\n2.15\n' $glibc_version | sort -V | head -n 1) != 2.15 ] && printf 'Version %s is lower than 2.15\n' $glibc_version || printf 'Version %s is equal or higher than 2.15\n' $glibc_version
Version 2.14 is lower than 2.15

答案3

不要将版本字符串转换为整数。比较字符串。

这对于您的用例来说有点过分了,但这里有一个比较典型版本字符串的 shell 函数。它遵循的一个子集Debian版本比较规则:

  • 版本是逐块比较的。每个块由最大字符序列组成,该字符序列要么完全由数字组成,要么不包含数字。
  • 非数字序列按字典顺序进行比较。
  • 数字序列根据十进制值进行比较。特别是,前导零是无关紧要的。

请注意,最后一点:1.01被视为等于1.1。这是要支付1.1并被1.9认为低于的价格1.10

此代码需要 dash、bash、ksh 或 zsh。要将其与其他 shell(例如 BusyBox sh)一起使用,请将对by 的调用替换为。[ "STRING1" \> "STRING2" ]expr "aSTRING1" \> "aSTRING2"

version_ge () (
  version1="$1" version2="$2"
  while true; do
    prefix1="${version1%%[0-9]*}" prefix2="${version2%%[0-9]*}"
    if [ "$prefix1" \> "$prefix2" ]; then return 0; fi
    if [ "$prefix2" \> "$prefix1" ]; then return 1; fi
    version1="${version1#"$prefix1"}" version2="${version2#"$prefix2"}"
    prefix1="${version1%%[!0-9]*}" prefix2="${version2%%[!0-9]*}"
    version1="${version1#"$prefix1"}" version2="${version2#"$prefix2"}"
    case "$prefix2" in
      0*[!0]*) prefix2="${prefix2##"${prefix2%%[!0]*}"}";;
      *[!0]*) :;;
      *) return 0;;
    esac
    case "$prefix1" in
      0*[!0]*) prefix1="${prefix1##"${prefix1%%[!0]*}"}";;
      *[!0]*) :;;
      *) return 1;;
    esac
    if [ "${#prefix1}" -gt "${#prefix2}" ]; then return 0; fi
    if [ "${#prefix1}" -lt "${#prefix2}" ]; then return 1; fi
    if [ "$prefix1" \> "$prefix2" ]; then return 0; fi
    if [ "$prefix2" \> "$prefix1" ]; then return 1; fi
  done
)

相关内容