检查Bash版本是否> =给定版本号


12

我需要测试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脚本中,我想以最优雅/最有效/最易读的方式进行单线测试,但也接受其他方法。


您是否查看了$BASH_VERSION$BASH_VERSINFO变量?
钢铁司机

@steeldriver不。如果您要发布bash特定案例,那将是一个可以接受的答案。但是对于其他情况,环境变量可能不可用。
WinEunuuchs2Unix


@SergiyKolodyazhnyy仅与bash有关。尽管我在寻找bash单行代码或脚本/函数,但仅检查bash版本号并不是最终目标。--version最初的意图是通过附加和测试输出来检查所有程序的通用例程。我已经相应地编辑了问题。
WinEunuuchs2Unix

版本号格式在各个程序之间差异很大。他们报告版本详细信息的方式也大相径庭。要求一种检查随机程序版本的方法根本不可行。这个问题太广泛了。
muru

Answers:


16

尝试:

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

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

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

您询问了有关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

好答案!请注意,您使用的uname -r是“ 4.9.0-2-amd64”,而实际上我的版本号更大,使用常规的bash测试可以测试比我的“ 4.11.1-041101-generic”更大。
WinEunuuchs2Unix

2
@ WinEunuuchs2Unix Python工具可以准确比较版本字符串。请参阅比较Python中的版本字符串
wjandrea

John--蟒例如可以与被缩短$ python --version,其返回Python 2.7.12。@wjandrea-感谢您的+1链接。也许我可以建立一个包含所有被称为程序名称和最小版本号的表。然后将表传递给python您提供的链接的修改后的副本。因为只有编译的Python可以被grub您调用,所以您会认为二进制文件可以在shell中执行此操作或可能的话。
WinEunuuchs2Unix

9

无需比较版本号,您可以直接测试功能本身。如果无法识别,则declare -A返回2(至少在Bash 3.2中)-A,因此进行测试(还会显示错误):

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

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

虽然我并没有真正假定任何人会在Bash中向后移植功能,但是通常来说,检查功能而不是版本是更合适的。即使在Bash的情况下,也可能有人会编译仅具有有限功能的版本...


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

首先是比较困难的一个。许多程序使用诸如--version或的命令行标志来告知其版本号-v,但是输出格式会有所不同,并且以编程方式选择版本号可能会很困难。然后就是可能同时安装同一程序的多个版本的问题。

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

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

5

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

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或没有apt安装(例如RHEL或FreeBSD),它将不会对您有任何帮助。

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

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

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

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

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


如第4节中所建议,关联数组是“两个数组”中的第二个。其唯一目的是保留键“ path / to / file-name”,并且值是主数组中的索引。创建关联数组是为了加快对当前顺序的常规索引数组的搜索速度。尽管我仍然关注Python,但我目前的bash兼容性主要集中在Ubuntu或Windows 10的Bash上。我喜欢可以定制的脚本。例如,yad --version返回,0.37.0 (GTK+ 3.18.9)但当前具有新功能0.39
WinEunuuchs2Unix

伟大而有益的答案。谢谢!
qodeninja

2

单线不可能,但是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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.