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 には、のデフォルト値を含めます。

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

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

バージョン番号を比較する代わりに、機能自体を直接テストすることができます。 は、認識されない場合 (少なくとも Bash 3.2 では)declare -Aを返すので、それをテストします (エラーも出力されます)。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) それを別の値と比較する方法の 2 つの部分があります。

1 つ目はより難しいものです。多くのプログラムは、--versionやなどのコマンド ライン フラグを使用してバージョン番号を指定します-vが、出力形式はさまざまであり、プログラムでバージョン番号を選択するのは難しい場合があります。また、同じプログラムの複数のバージョンが同時にインストールされている可能性があるという問題もあります。

2番目はバージョン番号の形式に関する知識に依存します。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。個人的には、次のようにサブシェルを使用します。

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

<<<ヒアドキュメントの構文は、/bin/shUbuntuでは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を使用してスクリプトを終了します。

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

この場合、ユーザーに「bash バージョン 4.3 以上が必要です。そうでないとスクリプトは動作しません」と明示的に伝える必要があります。その後の責任はユーザーにありますが、これはソフトウェア開発にとってあまり良いアプローチではないと主張する人もいるかもしれません。

4. すべての希望を捨てて、移植性のあるPOSIX準拠のスクリプトを書く

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

答え4

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

私は Stack Overflow の回答を参考にしたスクリプトを開発しました。その回答の 1 つがきっかけで、Dell の従業員が 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

関連情報