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


11

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変数?
スチールドライバー2017年

@steeldriverいいえ。投稿したい場合は、bashの特定のケースでこれは許容できる回答です。ただし、他の場合では、環境変数を使用できない場合があります。
WinEunuuchs2Unix 2017年


bashのみに関連する@SergiyKolodyazhnyy。私はbashのワンライナーまたはスクリプト/関数を探していますが、bashのバージョン番号のみをチェックすることは最終的な目標ではありません。--version出力を追加してテストすることによってすべてのプログラムをチェックする汎用ルーチンは、当初の目的でした。それに応じて質問を編集しました。
WinEunuuchs2Unix

バージョン番号の形式はプログラムによって大きく異なります。彼らがバージョンの詳細を報告する方法も大きく異なります。ランダムなプログラムのバージョンをチェックする方法を求めるのは、実際的ではありません。この質問は広すぎます。
muru

回答:


15

試してください:

$ [ "${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 2017年

2
@ WinEunuuchs2Unix Pythonツールは、バージョン文字列を正確に比較できます。Pythonでのバージョン文字列の比較
wjandrea

John-- Pythonの例は、$ python --versionwhichを返すことで短縮できますPython 2.7.12。@ wjandrea--リンク+1に感謝します。おそらく、呼び出されたすべてのプログラム名と最小限のバージョン番号の表を作成できます。次に、指定したpythonリンクの変更されたコピーにテーブルを渡します。コンパイルされたPythonしか呼び出せないためgrub、これを実行するためのバイナリが存在するか、シェルで実行できると考えられます。
WinEunuuchs2Unix

9

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

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

declare -A varvar非連想配列の場合は失敗するためunset、最初に失敗します。)

私は実際に誰かがBashの機能をバックポートすることを想定していませんが、一般に、バージョンではなく機能をチェックする方が適切です。バッシュの場合でさえ、誰かが限られた機能しか持たないバージョンをコンパイルするかもしれません...


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

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

2番目は、バージョン番号の形式に関する知識に依存します。Debianスタイルのバージョン番号(サブセットとしてsemverタイプのバージョンを含むと思います)をdpkg比較できます:

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変数の内容を確認するだけで十分です。個人的に私はサブシェルを次のように使用します:

$ (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

これは移植性がないことに注意してください。システム(RHELやFreeBSDなど)がインストールされていないdpkgapt、システムにインストールされていない場合は、何の役にも立ちません。

3.エラーが発生した場合は、set -eを使用してスクリプトを終了します

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

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

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

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


連想配列は、セクション4で提案する「2つの配列」の2番目です。唯一の目的は、キー「path / to / file-name」を保持することであり、値はメイン配列内のインデックスです。連想配列は、現在シーケンシャルな通常のインデックス付き配列への検索を高速化するために作成されました。とにかく私はPythonに目を向けていますが、現在のbashの互換性はUbuntuとおそらくWindows 10のBashに焦点を当てています。たとえば、yad --version返品はあります0.37.0 (GTK+ 3.18.9)が、新機能は現在にあり0.39ます。
WinEunuuchs2Unix 2017年

素晴らしくて役立つ答え。ありがとうございました!
qodeninja

2

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

Stack Overflowで回答を利用するスクリプトを開発しました。これらの回答の1つが、DKMSアプリケーションのバージョン番号の比較を2004年に書いているデルの従業員につながりました。

コード

以下の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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.