Перевірте, чи є версія Bash> = заданий номер версії


12

Мені потрібно перевірити, чи є номер версії 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_VERSIONта $BASH_VERSINFOзмінні?
steeldriver

@steeldriver Ні. Це було б прийнятною відповіддю в конкретному випадку, якщо ви хочете опублікувати його. Однак для інших випадків екологічні змінні можуть бути недоступними.
WinEunuuchs2Unix


@SergiyKolodyazhnyy, що стосується лише баш. Хоча я шукаю однофайлове bash або сценарій / функцію, що перевіряє лише номер версії bash, не є кінцевою метою. --versionПервісний намір було перевірити всі програми шляхом додавання та тестування результатів. Я відповідно відредагував питання.
WinEunuuchs2Unix

Формати номерів версій значно залежать від програм. Те, як вони повідомляють про деталі версій, також сильно відрізняються. Попросити спосіб перевірити версію випадкової програми просто не практично. Це питання занадто широке.
муру

Відповіді:


16

Спробуйте:

$ [ "${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 -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", який міг би перевірити більше, ніж мій "4.11.1-041101-generic", за допомогою звичайного баш-тесту, коли насправді номер моєї версії більший.
WinEunuuchs2Unix

2
@ WinEunuuchs2Unix Python інструменти можуть точно порівняти рядки версій. Див. Порівняти рядки версії в Python
wjandrea

Джон - приклад пітона можна скоротити, з $ python --versionяким повертається Python 2.7.12. @ wjandrea-- дякую за посилання +1. Можливо, я міг би створити таблицю з усіма назвами програм та мінімальними номерами версій. Потім передайте таблицю в модифіковану копію pythonнаданого вами посилання. Оскільки ви можете викликати лише складений Python, grubви б подумали, що існує бінарний файл, щоб зробити це або його можливо в оболонці.
WinEunuuchs2Unix

9

Замість того, щоб порівнювати номери версій, ви можете перевірити безпосередньо на саму функцію. 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, загалом, більш доцільно перевірити їх, а не версії. Навіть у випадку Баша хтось може скласти версію з обмеженими можливостями ...


Більш загальний випадок тестування номерів версій має дві частини: 1) як знайти правильний номер версії для тестування, і 2) як порівняти його з іншим значенням.

Перший - складніший. Багато програм повідомляють номер своєї версії за допомогою прапора командного рядка, наприклад, --versionабо -v, але формат виводу змінюється, а вибір програмного номера може бути важким. Тоді виникає проблема, можливо, встановити кілька версій однієї програми одночасно.

Друга залежить від певного знання формату номерів версій. 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

5

Є кілька способів наблизитись до того, що ви хочете досягти.

1. Використовуйте $ BASH_VERSION

Достатньо лише побачити, що є в $BASH_VERSIONзмінній. Особисто я би використовував підзаголовки так:

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

Зауважте, що <<<синтаксис для here-doc не є портативним, якщо ви збираєтеся ним користуватися /bin/sh, який є Dash на Ubuntu і може бути чимось іншим в іншій системі

Альтернативний спосіб - через випадок справи або 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 для виходу із сценарію, якщо є помилка

Один із способів її обійти - просто просто вперед використовувати асоціативні масиви та вийти, коли bashне можете їх використовувати. set -eрядок нижче #!/bin/bashдозволить скрипту вийти, якщо сценарій не може використовувати асоціативний масив.

Це зажадає від вас явно сказати користувачеві: "Ей, вам дійсно потрібна версія bash 4.3 або вище, інакше сценарій не працюватиме". Тоді відповідальність покладається на користувача, хоча деякі можуть стверджувати, що це не дуже вдалий підхід до розробки програмного забезпечення.

4. Відмовтеся від усієї надії та написати портативні, POSIX-сумісні сценарії

bashсценарії не є портативними, оскільки його синтаксис не сумісний з оболонкою Bourne. Якщо сценарій, який ви пишете, буде використовуватися в різних системах, не тільки в одній лише Ubuntu, тоді відмовтеся від будь-якої надії та знайдіть способи використовувати щось інше, ніж асоціативні масиви. Це може включати наявність двох масивів або розбір конфігураційного файла. Розглянемо також можливість переходу на іншу мову, Perl чи Python, де синтаксис принаймні більш портативний, ніж bash.


Асоціативний масив є другим із "двох масивів", як ви пропонуєте у розділі 4. Єдиною метою є утримання ключа "шлях / до / ім'я файлу", а значення - індекс у головному масиві. Асоціативний масив був створений для прискорення пошуку в регулярному індексованому масиві, які в даний час є послідовними. Хоча я все-таки приглядаюся до Python, моя поточна сумісність bash зосереджена на Ubuntu та, можливо, на Bash для Windows 10. Мені подобається ваш сценарій, який можна налаштувати. Наприклад, yad --versionповертається, 0.37.0 (GTK+ 3.18.9)але нові функції наразі є 0.39.
WinEunuuchs2Unix

чудова і корисна відповідь. Дякую тобі!
qodeninja

2

Не можливий однолінійний, але можливий сценарій bash

Я розробив сценарій, який спирається на відповіді в Stacka Overflow. Одна з таких відповідей призвела до порівняння номерів версії Dell Employee у 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.