Идентификация системного менеджера пакетов

Вопрос или проблема

Существует ли способ (из скрипта) определить менеджер пакетов по умолчанию в системе?

Чтобы уточнить, что я хочу сделать: запустить определенную команду, и на Debian или любом из его производных она вернет что-то вроде “apt”, на openSUSE вернет “zypp”, на Fedora и т. д. вернет “yum”, на Arch Linux вернет “pacman” и т. д.

Я знаю, что могу сделать это с помощью чего-то вроде следующего, но мне интересно, есть ли более надежный метод, который не сломается, как только появится исполняемый файл с таким же именем.

which apt >/dev/null 2>&1
if [ $? -eq 0 ]
then
    echo "apt"
fi
# и так далее...

Вместо идентификации программных бинарников, вам следует начать с определения дистрибутивов.

Вот несколько строк, которые работают в bash-скриптах:

declare -A osInfo;
osInfo[/etc/redhat-release]=yum
osInfo[/etc/arch-release]=pacman
osInfo[/etc/gentoo-release]=emerge
osInfo[/etc/SuSE-release]=zypp
osInfo[/etc/debian_version]=apt-get
osInfo[/etc/alpine-release]=apk

for f in ${!osInfo[@]}
do
    if [[ -f $f ]];then
        echo Package manager: ${osInfo[$f]}
    fi
done

Хотя эти файлы могут быть модифицированы так, что этот метод потерпит неудачу, это будет высоконестандартная конфигурация, поэтому это должно работать в большинстве ситуаций.

Я решил пойти по этому пути после изучения других. Это возникло у меня при запуске множества docker-контейнеров и необходимости в curl / jq, без уверенности в том, что они доступны из задания в задание.

script:
 - packagesNeeded='curl jq'
 - if [ -x "$(command -v apk)" ];       then sudo apk add --no-cache $packagesNeeded
 - elif [ -x "$(command -v apt-get)" ]; then sudo apt-get install $packagesNeeded
 - elif [ -x "$(command -v dnf)" ];     then sudo dnf install $packagesNeeded
 - elif [ -x "$(command -v zypper)" ];  then sudo zypper install $packagesNeeded
 - else echo "FAILED TO INSTALL PACKAGE: Package manager not found. You must manually install: $packagesNeeded">&2; fi

На чистом bash:

packagesNeeded=(curl jq)
if [ -x "$(command -v apk)" ];
then
    sudo apk add --no-cache "${packagesNeeded[@]}"
elif [ -x "$(command -v apt-get)" ];
then
    sudo apt-get install "${packagesNeeded[@]}"
elif [ -x "$(command -v dnf)" ];
then
    sudo dnf install "${packagesNeeded[@]}"
elif [ -x "$(command -v zypper)" ];
then
    sudo zypper install "${packagesNeeded[@]}"
else
    echo "FAILED TO INSTALL PACKAGE: Package manager not found. You must manually install: "${packagesNeeded[@]}"">&2;
fi

Вы также можете протестировать статус выхода command -v напрямую:

if command -v apk &> /dev/null
then
    sudo apk add ...
fi

Начните с принятого ответа на этот вопрос: Как получить имя дистрибутива и номер версии в простом shell-скрипте?. После этого решите, какой менеджер пакетов вы хотите использовать на основе обнаруженного дистрибутива.

Я использую эту функцию для предоставления зависимостей. Я определяю менеджер пакетов и их использование.

check_dependencies(){
    #Определить список зависимостей
    declare -Ag deps=([docker]='docker.io' [wg]='wireguard-tools' [lsof]='lsof')

    #Определить список менеджеров пакетов и их использование
    declare -Ag packman_list=([pacman]='pacman -Sy' [apt]='echo "deb http://deb.debian.org/debian buster-backports main contrib non-free" > /etc/apt/sources.list.d/buster-backports.list; apt update -y; apt install -y' [yum]='yum install -y epel-release; yum repolist -y; yum install -y')

    #Найти менеджер пакетов в системе и установить пакет
    install_deps(){
        for packman in ${!packman_list[@]}
        do
            which $packman &>/dev/null && eval $(echo ${packman_list[$packman]} "$*")
        done
    }

    #Найти отсутствующие пакеты из списка зависимостей
    declare -ag missing_deps=()
    for pack in ${!deps[@]}
    do
        which $pack &>/dev/null || missing_deps+=(${deps[$pack]})
    done

    #Установить отсутствующие зависимости
    test -z ${missing_deps[0]} || install_deps ${missing_deps[@]} || fail "Dependencies could not provide"
}

Я взял несколько идей отсюда и однострочник из другого места, чтобы получить дистрибутив, и у вас получилось это. Я использую это как часть сборок образов контейнеров. Это позволяет мне выполнять “общие” установки без необходимости заранее знать дистрибутив базового образа. Конечно, если имя пакета варьируется от дистрибутива к дистрибутиву, этот универсальный метод не работает так, как есть.

#!/bin/bash
pkg_install () {

local distro;local cmd;local usesudo
declare -A pkgmgr
pkgmgr=( \
[arch]="pacman -S --noconfirm" \
[alpine]="apk add --no-cache" \
[debian]="apt-get install -y" \
[ubuntu]="apt-get install -y" \
)

distro=$(cat /etc/os-release | tr [:upper:] [:lower:] | grep -Poi '(debian|ubuntu|red hat|centos|arch|alpine)' | uniq)
cmd="${pkgmgr[$distro]}"
[[ ! $cmd ]] && return 1
if [[ $1 ]]; then
[[ ! $EUID -eq 0 ]] && usesudo=sudo 
echo installing packages command: $usesudo $cmd $@
$usesudo $cmd $@
else 
echo $cmd
fi
}

# if script was executed then call the function
(return 0 2>/dev/null) || pkg_install "$@"

Вы можете выполнять его напрямую или подключать и вызывать функцию. Я не использую CentOS или Red Hat, но вы можете добавить их записи в массив или удалить те, которые не хотите поддерживать.

Мне пришлось написать версию POSIX (поскольку некоторые базовые образы, такие как Alpine, не имеют bash по умолчанию), так что вот она

#!/bin/sh
hm_hash() {
    echo "$1" | md5sum -
}

hm_put() {
    echo "$3" > "$1/$(hm_hash "$2")"
}

hm_get() {
    cat "$1/$(hm_hash "$2")"
}

pkg_install () {

local distro;local cmd;local usesudo; local pkgmgr
pkgmgr="$(mktemp -d)"
hm_put "$pkgmgr" arch "pacman -S --noconfirm" 
hm_put "$pkgmgr" alpine "apk add --no-cache"
hm_put "$pkgmgr" debian "apt-get install -y"
hm_put "$pkgmgr" ubuntu "apt-get install -y" 

distro=$(cat /etc/os-release | tr [:upper:] [:lower:] | grep -Poi '(debian|ubuntu|red hat|centos|arch|alpine)' | uniq)
cmd=$(hm_get "$pkgmgr" $distro 2> /dev/null)
rm -rf $pkgmgr
if [ ! -z "$cmd" ]; then
    if [ -z "$1" ]; then
    echo $cmd
    else 
    echo installing packages command: $cmd $@
    $cmd $@
    fi
 else  
   return 1  
 fi  
}

# if script was executed then call the function
(return 0 2>/dev/null) || pkg_install "$@"

Добавляю этот скрипт, чтобы перечислить все* установленные менеджеры пакетов, на случай, если это кому-то будет полезно. * Возможно, я упустил некоторые.

#!/usr/bin/env bash
echo Installed package managers:
echo
for pm in apk app apt apt-get cargo dnf dpkg emerge eopkg flatpak nala moss nix-env pacman pamac portage rpm snap xbps yay yum zypper; do
    command -v $pm
done

Ответ или решение

Идентификация системного пакетного менеджера в большинстве дистрибутивов Linux может быть сложной задачей, особенно когда требуется использовать автоматические скрипты для установки программ. Знание того, какой пакетный менеджер использовать на конкретной системе, является ключевым аспектом для автоматизации процессов установки ПО, особенно при работе с контейнерами или в окружении, где заранее неизвестно, на каком дистрибутиве будет запущен скрипт.

Теория (T)

Менеджеры пакетов — это инструменты, позволяющие автоматизировать процесс установки, обновления и удаления программного обеспечения на операционной системе. Каждая Linux-система, как правило, использует один или несколько пакетных менеджеров, специфичных для ее дистрибутива. Например, apt используется в Debian и его производных (таких как Ubuntu), yum или dnf в Fedora и CentOS, pacman в Arch Linux, zypper в openSUSE и так далее.

Пример (E)

Представим сценарий, при котором IT-специалисту необходимо автоматически определить, какой менеджер пакетов используется на текущей системе, и на основе этой информации выполнить команду установки определенных пакетов. В случае с пакетом управления конфигурациями, например, может потребоваться установка таких инструментов, как curl или wget, без необходимости вручную проверять и адаптировать скрипт под каждый дистрибутив.

Проблема в том, что использование команды which <менеджер пакетов> не всегда надежно, так как на системе может оказаться установленными несколько менеджеров пакетов, и они могут быть не настроены должным образом (например, в контейнерах). Вместо этого рекомендованный подход заключается в определении дистрибутива на основе информации из специфических для системы файлов.

Применение (A)

Для надежного определения менеджера пакетов можно использовать следующий подход. Скрипт идентифицирует дистрибутив, сначала анализируя содержимое файлов, таких как /etc/os-release, либо используя специальную карту соответствия файлов релизов соответствующим менеджерам пакетов. Ниже представлен пример такого скрипта на языке bash:

#!/bin/bash

# Создаем ассоциативный массив для соответствия файлов дистрибутива менеджерам пакетов
declare -A osInfo;
osInfo[/etc/redhat-release]=yum
osInfo[/etc/arch-release]=pacman
osInfo[/etc/gentoo-release]=emerge
osInfo[/etc/SuSE-release]=zypper
osInfo[/etc/debian_version]=apt

for f in ${!osInfo[@]}
do
    if [[ -f $f ]]; then
        echo "Менеджер пакетов: ${osInfo[$f]}"
        break
    fi
done

Данный скрипт читает файлы, специфичные для дистрибутива, и на их основе выводит используемый менеджер пакетов. Это наилучшим образом подходит для определенных случаев, помогая избежать проблем, возникающих при наличии более одного менеджера на системе.

Расширенное использование

Для работы в различных окружениях, например, в Docker-контейнерах, где важно автоматизировать процесс установки, использование комбинации command -v для определения доступного менеджера пакетов также может быть полезно:

packagesNeeded="curl jq"
if command -v apt-get &>/dev/null; then
    sudo apt-get update -y && sudo apt-get install -y $packagesNeeded
elif command -v dnf &>/dev/null; then
    sudo dnf install -y $packagesNeeded
elif command -v pacman &>/dev/null; then
    sudo pacman -Sy --noconfirm $packagesNeeded
# Добавьте дополнительные проверки для других менеджеров, если требуется
else
    echo "Менеджер пакетов не найден. Ручная установка пакетов: $packagesNeeded">&2
fi

Таким образом, правильный подход к идентификации системного пакетного менеджера, основанный на анализе дистрибутива, обеспечивает более надежную и универсальную работу скриптов и автоматических процессов установки программного обеспечения в разных дистрибутивах Linux.

Оцените материал
Добавить комментарий

Капча загружается...