#StackBounty: #linux #bash #package-management Distribution-agnostic package search and installation

Bounty: 50

I’m trying to figure out a distribution-agnostic way to install packages providing specific executables or files. I know that it is impossible to get this perfect, but I think I have something that is almost good enough and I was hoping that maybe someone has an idea on how to improve upon it.

Basically I’ve written a script that provides an abstraction layer around the most common package managers:

commandAvailable() { command -v $1 &> /dev/null; }

if commandAvailable dnf; then
    updatePackageInfo() { dnf check-update; }
    searchPath() { dnf provides $1 2> /dev/null | grep ' : ' | head -1 | cut -d'.' -f1 | rev | cut -d'-' -f 2- | rev; }
    searchBin() { searchPath {/bin,/sbin,/usr/bin/,/usr/sbin}/$1; }
    install() { dnf install -y $@; }
elif commandAvailable yum; then
    updatePackageInfo() { yum check-update; }
    searchPath() { yum provides $1 2> /dev/null | grep ' : ' | head -1 | cut -d'.' -f1 | rev | cut -d'-' -f 2- | rev; }
    searchBin() { searchPath {/bin,/sbin,/usr/bin/,/usr/sbin}/$1; }
    install() { yum install -y $@; }
elif commandAvailable apt-get; then
    updatePackageInfo() { apt-get update && if ! commandAvailable apt-file; then install apt-file; fi && apt-file update; }
    searchPath() { apt-file search $1 | head -1 | cut -d':' -f1; }
    searchBin() { searchPath {/bin,/sbin,/usr/bin/,/usr/sbin}/$1; }
    install() { apt-get install -y $@; }
elif commandAvailable pacman; then
    updatePackageInfo() { pacman -Sy && pacman -Fy; }
    searchPath() { pacman -F $1 | head -1 | rev | cut -d' ' -f2 | rev; }
    searchBin() { pacman -F $1 | grep -B 1 -P "    (usr/bin|usr/sbin|bin|sbin)/$1" | head -1 | cut -d' ' -f1; }
    install() { pacman -S --noconfirm $@; }
elif commandAvailable zypper; then
    updatePackageInfo() { zypper refresh; }
    searchPath() { zypper search -f $1 | grep " | package" | head -1 | tr -d ' ' | cut -d'|' -f2; }
    searchBin() { searchPath {/bin,/sbin,/usr/bin/,/usr/sbin}/$1; }
    install() { zypper --non-interactive install "$@"; }
elif commandAvailable emerge; then
    updatePackageInfo() { emerge-webrsync -v && if ! commandAvailable e-file; then install app-portage/pfl; fi; }
    searchPath() { e-file $1 | grep -P "([I]| * )" | sed 's/*//g' | sed 's/[I]//g' | tr -d ' '; }
    searchBin() { searchPath /usr/bin/$1; searchPath /usr/sbin/$1; searchPath /bin/$1; searchPath /sbin/$1; }
    install() { emerge $@; }
fi

searchBins() { for executable in "$@"; do searchBin $executable; done | tr "n" " "; echo; }
searchPaths() { for path in "$@"; do searchPath $path; done | tr "n" " "; echo; }
installPkgWithPath() { install $(searchPath "$1"); }
installPkgsWithPaths() { install $(searchPaths $@); }
installPkgWithExecutable() { install $(searchBin $1); }
installPkgsWithExecutables() { install $(searchBins $@); }

The functions it creates can be used like this:

updatePackageInfo                                  # Equivalent to apt-get update

installPkgWithPath "curl/curl.h"                   # Installs the package containing the header file curl/curl.h

installPkgsWithPaths "curl/curl.h" "/usr/bin/wget" # Installs multiple packages by file paths

installPkgWithExecutable curl                      # Install the package that provides the `curl` executable

installPkgsWithExecutables curl wget make          # Install all packages required to get these 3 executables

This basically works fine on Fedora, RHEL, Debian, Arch, Gentoo, OpenSuse (and many more I would think). But it does not work on Ubuntu for example because Ubuntu doesn’t have the required apt-file package (unless you enable the Universe repository that provides it).
Another thing that doesn’t work is installing things like vlc in Fedora. On Fedora you would usually enable the RPM Fusion repositories for that.
And I’m sure other distributions have similar situations.

What I have considered is maybe adding a --force flag that, when set, causes these repositories to be searched and added if needed. But I would hate to end up in a situation where I have to maintain lists of repositories. My hope is that most distributions somehow reference these semi-trusted repositories in a way that I don’t have to maintain a list of repositories for every distribution.

Any ideas?


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.