
我需要測試 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 -c 'import platform; print(platform.python_version())'
2.7.13
若要取得核心版本,請使用uname
:
$ uname -r
4.9.0-2-amd64
答案2
您可以直接測試功能本身,而不是比較版本號。如果它不能識別 則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 風格的版本號(我認為包括塞姆弗輸入版本作為子集):
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
請注意,<<<
如果您要將其與/bin/sh
Ubuntu 上的 Dash 一起使用,則here-doc的語法不可移植,在不同的系統上可能是其他東西
另一種方法是透過 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