29

Is there a way (from a script) to identify the default system package manager?

To clarify, what I want to do is run a given command and, on Debian or any of its derivatives it'll return something like "apt", on openSUSE it'll return "zypp", on Fedora et al it'll return "yum", on Arch Linux it'll return "pacman" etc.

I know I can do this with something like the following, I just wondered if there was a more robust method that won't break as soon as there is an executable with the same name.

which apt >/dev/null 2>&1
if [ $? -eq 0 ]
then
    echo "apt"
fi
# etc...

5 Answers 5

23

Instead of identifying binary programs, you should start from identifying distributions.

Hare are a few lines that works in bash scripting:

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

Althrough it is possible for these files to be modified so that this method will fail, that would be a highly non-standard configuration so this should work in most situations.

1
  • "Althrough these parts can't be trusted". In what sense? Aug 26, 2022 at 10:21
10

Start with the accepted answer to this question: How can I get distribution name and version number in a simple shell script?. Then, decide which package manager you want to use based on the detected distribution.

5
  • Is there a readily available list of package managers for different distributions? There are a lot of debian clones in particular.
    – DrAl
    Aug 23, 2012 at 6:58
  • Not sure about a readily available list, but all Debian clones will use apt-get.
    – Jim Paris
    Aug 23, 2012 at 16:52
  • Yes, I realise that; my problem isn't so much identifying the distribution (although your link will certainly make this easier), it's figuring out how to link a distribution name like (to pick one at random) "SolusOS" with apt without maintaining a list of every distribution that exists.
    – DrAl
    Aug 24, 2012 at 6:49
  • Do it the autotools way: Check if it works. BTW, that a distribution uses apt means that it is a Debian relative (and they are a rather compact bunch), finding yum doesn't mean that the packaging conventions (split into library/development, one big brick of Perl or individual packages off CPAN, ...) are the same. Don't even try to install a Fedora package on CentOS (there are at least 3 or 4 years of version skew, just won't work).
    – vonbrand
    Jan 18, 2013 at 3:28
  • 1
    Detecting the distro will only work for the popular ones. A whitelist can't be updated for all the less know distributions. Directly testing the presence of the tools is the only way to make it work. If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
    – liberforce
    Oct 18, 2018 at 9:53
10

I opted to go this route after reviewing the others. This came up for me when running many docker containers and needing curl / jq and not being able to rely on what was available from job to job.

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

In pure 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

You can also test the exit status of command -v directly:

if command -v apk &> /dev/null
then
    sudo apk add ...
fi
1
  • I'll note in GitLab CI I had some issue stacking the 'elif' for some reason which is why I just if statements.
    – Mark
    May 5, 2020 at 21:23
1

I am using this function to provide dependencies. I am defining package manager and their how to use.

check_dependencies(){
    #Declare list of dependencies
    declare -Ag deps=([docker]='docker.io' [wg]='wireguard-tools' [lsof]='lsof')

    #Declare list of package managers and their usages
    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')
    
    #Find the package manager on the system and install the package
    install_deps(){
        for packman in ${!packman_list[@]}
        do
            which $packman &>/dev/null && eval $(echo ${packman_list[$packman]} "$*")
        done
    }
    
    #Find the missing packages from list of dependencies
    declare -ag missing_deps=()
    for pack in ${!deps[@]}
    do
        which $pack &>/dev/null || missing_deps+=(${deps[$pack]})
    done

    #Install missing dependencies
    test -z ${missing_deps[0]} || install_deps ${missing_deps[@]} || fail "Dependencies could not provide"
}
0

I took some ideas from here and a one liner from elsewhere to get the distro and you have this. I'm using this as part of container image builds. Let's me do "generic" installs without having to know apriori the distro of the base image. Of course if a package name varies from distro to distro this one fits all fails as is.

#!/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 "$@"

you can run it directly or source it and call the function. I don't use centos or redhat but you can add their entries to the array, or remove ones you don't want to support.

I had to write a posix version (because some base images like alpine don't have bash installed by default) so here is that

#!/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 "$@"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .