我需要测试 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
也会失败,因此它首先失败。)var
unset
虽然我并不认为有人会反向移植 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
变量中的内容就足够了。我个人会像这样使用 subshell:
$ (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