web-dev-qa-db-ja.com

Bashバージョンが指定されたバージョン番号以上かどうかを確認します

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スクリプトでは、可能な限り最もエレガント/効率的/読みやすい方法でワンライナーテストを行いたいが、他のアプローチも受け入れられます。

10

試してください:

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

BASH_VERSINFOは、bashのこのインスタンスのバージョン情報を保持するメンバーを持つ読み取り専用の配列変数です。これはbash 2.0で導入されたため、おそらく遭遇するすべてのbashバージョンでサポートされています。ただし、注意するために、この変数が設定されていない以前のバージョンのbashには0のデフォルト値を含めます。

他のプログラムからバージョン情報を抽出する

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
13
John1024

バージョン番号を比較する代わりに、機能自体を直接テストできます。 declare -Aは、2を認識しない場合は-A(少なくともBash 3.2で)を返すので、テストします(エラーも出力します)。

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

varが非連想配列の場合、declare -A varも失敗するため、最初にunsetになります。)

誰もがBashの機能をバックポートするとは思いませんが、一般的には、バージョンではなく機能をチェックする方が適切です。 Bashの場合でも、誰かが限定された機能のみを備えたバージョンをコンパイルする可能性があります...


バージョン番号をテストするより一般的なケースには、2つの部分があります。1)テストする正しいバージョン番号を見つける方法、2)別の値と比較する方法。

最初はもっと難しいものです。多くのプログラムは--version-vのようなコマンドラインフラグでバージョン番号を伝えますが、出力形式はさまざまであり、プログラムでバージョン番号を選択するのは難しい場合があります。次に、同じプログラムの複数のバージョンが同時にインストールされる可能性があるという問題があります。

2番目は、バージョン番号の形式に関する知識に依存します。 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
8
ilkkachu

達成したいものにアプローチする方法はいくつかあります。

1. $ BASH_VERSIONを使用します

$BASH_VERSION変数の内容を確認するだけで十分です。個人的に私は次のようなサブシェルを使用します:

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

Here-docの<<<構文は、/bin/shと共に使用する場合、移植性がないことに注意してください。これは、Ubuntuではダッシュであり、別のシステムでは別のものになる可能性があります

別の方法は、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を使用してスクリプトを終了します

これを回避する1つの方法は、単に連想配列を使用して、bashが使用できないときに終了することです。 set -eの下の#!/bin/bash行は、スクリプトが連想配列を使用できない場合にスクリプトを終了できるようにします。

そのためには、ユーザーに「bashバージョン4.3以降が本当に必要です。そうしないと、スクリプトが機能しません」と明示的にユーザーに伝える必要があります。責任はユーザーにありますが、これは実際にはソフトウェア開発への良いアプローチではないと主張する人もいるかもしれません。

4.すべての希望を捨てて、移植可能なPOSIX準拠のスクリプトを書く

bashスクリプトは、構文がBourne Shellと互換性がないため、移植できません。書いているスクリプトがUbuntuだけでなく、さまざまなシステムで使用される場合、すべての希望を捨てて、連想配列以外のものを使用する方法を見つけてください。これには、2つの配列を持つことや、構成ファイルを解析することが含まれます。構文が少なくともbashより移植性の高い別の言語、PerlまたはPythonへの切り替えも検討してください。

5

ワンライナーは不可能ですが、bashスクリプトは可能です

Stack Overflowで答えを引き出すスクリプトを開発しました。これらの回答の1つは、2004年にDKMSアプリケーションのバージョン番号の比較を作成するDell従業員につながりました。

コード

以下の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
2