1. 使用 $BASH_VERSION

1. 使用 $BASH_VERSION

我需要测试 Bash 版本号是否 >= 特定数字。例如,我有:

$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

为了使用关联数组,bash 版本号必须> = 4。

在我的 bash 脚本中,我想以最优雅/最高效/最易读的方式进行单行测试,但其他方法也可以接受。

答案1

尝试:

$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays

BASH_VERSINFO是一个只读数组变量,其成员保存此 bash 实例的版本信息。由于它是在 bash 2.0 中引入的,因此您遇到的所有 bash 版本都可能支持它。但为了谨慎起见,我们0为任何未设置此变量的早期版本的 bash 都提供了默认值 of。

从其他程序中提取版本信息

您询问了有关 LibreOffice、Python、内核等的问题。

LibreOffice 生成的版本信息如下:

$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)

提取版本号:

$ libreoffice --version | cut -d' ' -f2
5.2.6.2

对于python:

$ python -c 'import platform; print(platform.python_version())'
2.7.13

要获取内核版本,请使用uname

$ uname -r
4.9.0-2-amd64

答案2

您可以直接测试功能本身,而不必比较版本号。如果它无法识别,declare -A则会返回(至少在 Bash 3.2 中) ,因此请对其进行测试(它也会打印错误):2-A

unset assoc
if ! declare -A assoc ; then
    echo "associative arrays not supported!"
    exit 1
fi

(如果是非关联数组declare -A var也会失败,因此它首先失败。)varunset

虽然我并不认为有人会反向移植 Bash 的功能,但一般来说,检查功能而不是版本更为合适。即使在 Bash 的情况下,有人也可能会编译仅具有有限功能的版本...


测试版本号的更一般情况包括两个部分:1)如何找到正确的版本号进行测试,2)如何将其与另一个值进行比较。

第一个比较难。许多程序使用命令行标志(如--version或)来指示其版本号-v,但输出格式各不相同,并且以编程方式选择版本号可能很困难。然后是可能同时安装同一程序的多个版本的问题。

第二个取决于对版本号格式的一些了解。dpkg可以比较Debian 风格的版本号(我认为包括语义版本类型版本作为子集):

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "it's version 4.x"
fi

或者,仅将以上内容结合起来:

bashver=$( bash --version | sed -Ee 's/GNU bash, version ([0-9.]+).*/\1/;q' )
if dpkg --compare-versions "$bashver" ge 4.0.0 ; then
    echo "'bash' in your path is version 4.x"
fi

答案3

有几种方法可以实现您想要实现的目标。

1. 使用 $BASH_VERSION

只需查看$BASH_VERSION变量中的内容就足够了。我个人会像这样使用 subshel​​l:

$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4

请注意,<<<如果您要将其与 一起使用,则 here-doc 的语法是不可移植的,/bin/sh在 Ubuntu 上它是 Dash,在其他系统上可能是其他东西

另一种方法是通过 case 语句或 if 语句。就我个人而言,我会这样做:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays

可能为了可移植性,您可能应该首先检查是否设置了此类变量,例如[ -n $BASH_VERSION ]

这完全可以重写为在脚本中使用的函数。类似于以下内容:

#!/bin/bash
version_above_4(){
    # check if $BASH_VERSION is set at all
    [ -z $BASH_VERSION ] && return 1
   
    # If it's set, check the version
    case $BASH_VERSION in 
        4.*) return 0 ;;
        ?) return 1;; 
    esac
}

if version_above_4
then
    echo "Good"
else
    echo "No good"
fi

这不是一句俏皮话,尽管这样更好。质量胜过数量。

2. 检查已安装的内容

为此,您需要apt-cache policy像这样过滤输出

$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4

dpkg-query也可以派上用场的一些过滤通过awk

$ dpkg-query -W bash | awk '{print substr($2,1,1)}'   
4

请注意,这不是可移植的,因为如果系统上没有dpkg安装(例如,RHEL 或 FreeBSD),它就不会给你带来任何好处。apt

3. 如果出现错误,使用 set -e 退出脚本

解决这个问题的一种方法是简单地继续使用关联数组,当bash不能使用它们时退出。如果脚本不能使用关联数组,set -e下面的行将允许脚本退出。#!/bin/bash

这就要求你明确地告诉用户:“嘿,你确实需要 bash 4.3 或更高版本,否则脚本将无法工作”。然后责任就落在了用户身上,尽管有些人可能会认为这并不是一个好的软件开发方法。

4. 放弃所有希望,编写可移植的、符合 POSIX 标准的脚本

bash脚本不可移植,因为其语法与 Bourne shell 不兼容。如果您编写的脚本将在一系列不同的系统上使用,而不仅仅是 Ubuntu,那么放弃所有希望,并找到使用关联数组以外的其他方法的方法。这可能包括拥有两个数组或解析配置文件。还可以考虑切换到其他语言,Perl 或 Python,其中语法至少比 更易于移植bash

答案4

单行脚本不可行,但可以使用 Bash 脚本

我开发了一个脚本,该脚本借鉴了 Stack Overflow 上的答案。其中一个答案促使戴尔员工在 2004 年为 DKMS 应用程序编写了版本号比较。

代码

下面的 bash 脚本需要使用命令标记为可执行chmod a+x script-name。我使用的名称是/usr/local/bin/testver

#!/bin/bash

# NAME: testver
# PATH: /usr/local/bin
# DESC: Test a program's version number >= to passed version number
# DATE: May 21, 2017. Modified August 5, 2019.

# CALL: testver Program Version

# PARM: 1. Program - validated to be a command
#       2. Version - validated to be numberic

# NOTE: Extracting version number perl one-liner found here:
#       http://stackoverflow.com/questions/16817646/extract-version-number-from-a-string

#       Comparing two version numbers code found here:
#       http://stackoverflow.com/questions/4023830/how-compare-two-strings-in-dot-separated-version-format-in-bash

# Map parameters to coder-friendly names.
Program="$1"
Version="$2"

# Program name must be a valid command.
command -v $Program >/dev/null 2>&1 || { echo "Command: $Program not found. Check spelling."; exit 99; }

# Passed version number must be valid format.
if ! [[ $Version =~ ^([0-9]+\.?)+$ ]]; then
    echo "Version number: $Version has invalid format. Aborting.";
    exit 99
fi

InstalledVersion=$( "$Program" --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
# echo "Installed Version: $InstalledVersion"

if [[ $InstalledVersion =~ ^([0-9]+\.?)+$ ]]; then
    l=(${InstalledVersion//./ })
    r=(${Version//./ })
    s=${#l[@]}
    [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}

    for i in $(seq 0 $((s - 1))); do
        # echo "Installed ${l[$i]} -gt Test ${r[$i]}?"
        [[ ${l[$i]} -gt ${r[$i]} ]] && exit 0 # Installed version > test version.
        [[ ${l[$i]} -lt ${r[$i]} ]] && exit 1 # Installed version < test version.
    done

    exit 0 # Installed version = test version.
else
    echo "Invalid version number found for command: $Program"
    exit 99
fi

echo "testver - Unreachable code has been reached!"
exit 255

相关内容