Periksa apakah versi Bash>> diberikan nomor versi

11

Saya perlu menguji apakah nomor versi Bash>> ke nomor tertentu. Misalnya saya punya:

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

Untuk menggunakan array asosiatif, nomor versi bash harus>> 4.

Dalam skrip bash saya, saya ingin melakukan tes satu-liner dengan cara yang paling elegan / efisien / mudah dibaca, tetapi pendekatan lain juga diterima.

WinEunuuchs2Unix
sumber
Sudahkah Anda melihat variabel $BASH_VERSIONdan $BASH_VERSINFO?
steeldriver
@steeldriver No. Itu akan menjadi jawaban yang dapat diterima dalam kasus spesifik bash jika Anda ingin mempostingnya. Namun untuk kasus lain, variabel lingkungan mungkin tidak tersedia.
WinEunuuchs2Unix
Ngomong-ngomong, terkait: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy yang terkait dengan bash saja. Meskipun saya mencari bash one-liner atau skrip / fungsi memeriksa nomor versi bash saja bukan tujuan akhir. Rutin umum untuk memeriksa semua program dengan menambahkan --versiondan menguji output adalah niat asli. Saya telah mengedit pertanyaan sesuai.
WinEunuuchs2Unix
Format nomor versi sangat bervariasi antar program. Cara mereka melaporkan detail versi juga sangat bervariasi. Sama sekali tidak praktis untuk meminta cara memeriksa versi program acak. Pertanyaan ini terlalu luas.
muru

Jawaban:

15

Mencoba:

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

BASH_VERSINFOadalah variabel array hanya baca yang anggotanya memegang informasi versi untuk instance bash ini. Karena ini diperkenalkan dengan bash 2.0, kemungkinan didukung oleh semua versi bash yang akan Anda temui. Namun, untuk berhati-hati, kami menyertakan nilai default sebesar0 untuk setiap versi bash sebelumnya yang variabel ini tidak disetel.

Mengekstraksi informasi versi dari program lain

Anda bertanya tentang LibreOffice, Python, kernel, dll.

LibreOffice menghasilkan informasi versi yang terlihat seperti:

$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)

Untuk mengekstrak nomor versi:

$ libreoffice --version | cut -d' ' -f2
5.2.6.2

Untuk python:

$ python -c 'import platform; print(platform.python_version())'
2.7.13

Untuk mendapatkan versi kernel, gunakan uname:

$ uname -r
4.9.0-2-amd64
John1024
sumber
Jawaban bagus! Catat saja bahwa Anda uname -radalah "4.9.0-2-amd64" yang dapat menguji lebih besar dari milik saya "4.11.1-041101-generic" dengan tes bash reguler ketika kenyataannya nomor versi saya lebih besar.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Alat Python dapat secara akurat membandingkan string versi. Lihat Bandingkan string versi dengan Python
wjandrea
John - contoh python dapat disingkat dengan $ python --versionyang mengembalikan Python 2.7.12. @ wjandrea-- terima kasih untuk tautan +1. Mungkin saya bisa membuat tabel semua nama program dan nomor versi minimal. Kemudian, berikan tabel ke salinan yang dimodifikasi dari pythontautan yang Anda berikan. Karena hanya Python yang dikompilasi yang dapat dipanggil oleh grubAnda akan berpikir biner ada untuk melakukan ini atau kemungkinan dalam shell.
WinEunuuchs2Unix
9

Alih-alih membandingkan nomor versi, Anda dapat menguji langsung fitur itu sendiri. declare -Amengembalikan 2(setidaknya dalam Bash 3.2) jika tidak dikenali -A, jadi uji untuk itu (juga mencetak kesalahan):

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

( declare -A varjuga gagal jika vararray non-asosiatif, jadiunset yang pertama.)

Meskipun saya tidak benar-benar berasumsi siapa pun akan mendukung fitur di Bash, secara umum lebih tepat untuk memeriksa fitur, bukan versi. Bahkan dalam kasus Bash, seseorang mungkin mengompilasi versi dengan hanya fitur terbatas ...


Kasus yang lebih umum dari pengujian nomor versi memiliki dua bagian: 1) bagaimana menemukan nomor versi yang benar untuk diuji, dan 2) bagaimana membandingkannya dengan nilai lain.

Yang pertama adalah yang lebih sulit. Banyak program memberi tahu nomor versi mereka dengan bendera baris perintah seperti --versionatau-v , tetapi format output bervariasi dan secara terprogram memilih nomor versi mungkin sulit. Lalu ada masalah kemungkinan beberapa versi dari program yang sama diinstal pada waktu yang sama.

Yang kedua tergantung pada beberapa pengetahuan tentang format nomor versi. dpkgdapat membandingkan nomor versi gaya-Debian (yang saya pikir menyertakan versi jenis semver sebagai subset):

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "it's version 4.x"
fi

Atau, cukup dengan menggabungkan di atas:

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
ilkkachu
sumber
5

Ada beberapa cara untuk mendekati apa yang ingin Anda capai.

1. Gunakan $ BASH_VERSION

Cukup hanya dengan melihat apa yang ada di $BASH_VERSIONvariabel. Secara pribadi saya akan menggunakan subkulit seperti:

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

Perhatikan bahwa <<<sintaks untuk sini-doc tidak portabel, jika Anda akan menggunakannya /bin/sh, yaitu Dash pada Ubuntu dan mungkin sesuatu yang lain pada sistem yang berbeda

Cara alternatif adalah melalui pernyataan kasus atau pernyataan jika. Secara pribadi, saya akan melakukan ini:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays

Mungkin demi portabilitas, Anda mungkin harus memeriksa apakah variabel tersebut bahkan diset sama sekali di tempat pertama dengan sesuatu seperti [ -n $BASH_VERSION ]

Ini sepenuhnya dapat ditulis ulang sebagai fungsi yang akan digunakan dalam skrip. Sesuatu yang panjang dalam garis-garis:

#!/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

Ini bukan satu-liner, meskipun ini jauh lebih baik. Kualitas diatas kuantitas.

2. Periksa apa yang diinstal

Untuk itu Anda perlu memfilter keluaran apt-cache policysejenisnya

$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4

dpkg-queryjuga dapat berguna dengan beberapa penyaringan via awk.

$ dpkg-query -W bash | awk '{print substr($2,1,1)}'   
4

Perhatikan bahwa ini tidak portabel, karena jika tidak ada dpkgatau aptdiinstal pada sistem (misalnya, RHEL atau FreeBSD), itu tidak akan ada gunanya bagimu.

3. Gunakan set -e untuk keluar dari skrip jika ada kesalahan

Salah satu cara untuk menyiasatinya hanyalah dengan menggunakan array asosiatif dan berhenti ketika bashtidak dapat menggunakannya. set -ebaris di bawah ini #!/bin/bashakan memungkinkan skrip untuk berhenti jika skrip tidak dapat menggunakan array asosiatif.

Ini akan mengharuskan Anda untuk memberi tahu pengguna secara eksplisit: "Hei, Anda benar-benar membutuhkan bash versi 4.3 atau lebih baru, jika tidak skrip tidak akan berfungsi". Kemudian tanggung jawab terletak pada pengguna, meskipun beberapa orang mungkin berpendapat bahwa ini bukan pendekatan yang baik untuk pengembangan perangkat lunak.

4. Abaikan semua harapan dan tulis skrip portabel yang sesuai dengan POSIX

bashskrip tidak portabel karena sintaksisnya tidak kompatibel dengan Bourne shell. Jika skrip yang Anda tulis akan digunakan pada berbagai sistem yang berbeda, bukan hanya Ubuntu saja, tinggalkan semua harapan, dan temukan cara untuk menggunakan sesuatu selain array asosiatif. Itu mungkin termasuk memiliki dua array atau parsing file konfigurasi. Pertimbangkan juga beralih ke bahasa lain, Perl atau Python, di mana sintaks setidaknya lebih portabel daripada bash.

Sergiy Kolodyazhnyy
sumber
Array asosiatif adalah yang kedua dari "dua array" seperti yang Anda sarankan di bagian 4. Tujuan utamanya adalah untuk menahan kunci "path / ke / file-name" dan nilainya adalah indeks dalam array utama. Array asosiatif dibuat untuk mempercepat pencarian ke dalam array berindeks biasa yang saat ini berurutan. Meskipun saya tetap memperhatikan Python, kompatibilitas bash saya saat ini terfokus pada Ubuntu dan mungkin Bash untuk Windows 10. Saya suka skrip Anda yang dapat disesuaikan. Misalnya yad --versionmengembalikan 0.37.0 (GTK+ 3.18.9)tetapi fitur baru sedang dalam 0.39.
WinEunuuchs2Unix
jawaban yang bagus dan bermanfaat. Terima kasih!
qodeninja
2

Satu baris tidak mungkin tetapi skrip bash dimungkinkan

Saya mengembangkan skrip yang mengacu pada jawaban di Stack Overflow. Salah satu jawaban itu mengarah ke perbandingan nomor versi Karyawan Dell pada tahun 2004 untuk aplikasi DKMS.

Kode

Skrip bash di bawah ini harus ditandai sebagai dapat dieksekusi menggunakan perintah chmod a+x script-name. Saya menggunakan namanya /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
WinEunuuchs2Unix
sumber