#!/bin/bash # Purpose: A wrapper for all Unix package managers # Author : Anh K. Huynh # License: Fair license (http://www.opensource.org/licenses/fair) # Source : http://github.com/icy/pacapt/ # Copyright (C) 2010 - 2013 Anh K. Huynh # # Usage of the works is permitted provided that this instrument is # retained with the works, so that any entity that uses the works is # notified of this instrument. # # DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. _POPT="" # primary operation _SOPT="" # secondary operation _TOPT="" # options for operations _PKG="" # packages and extra parameters for apt-get _VERBOSE="" # verbose mode _FORCE="" # force yes _OSTYPE="" # type of package manager (Arch pacman, Debian apt, ...) _error() { echo >&2 ":: $*" } _help() { cat <<\EOF NAME pacapt - An Arch's pacman-like package manager for some Unices. DESCRIPTION An Arch's pacman-like package manager for some Unices. Actually this Bash script provides a wrapper for system's package manager. Instead of remembering various options/tools on different OSs, you only need a common way to manipulate packages. Not all options of the native package manager are ported; the tool only provides a very basic interface to search, install, remove packages, and/or update the system. Arch's pacman is chosen, as pacman is quite smart when it divides all packages-related operations into three major groups: Synchronize, Query and Remove/Clean up. It has a clean man page, and it is the only tool needed to manipulate official packages on system. (Debian, for example, requires you to use apt-get, dpkg, and/or aptitude.) The tool supports the following package managers: pacman by Arch Linux, ArchBang, Manjaro, etc. dpkg/apt-get by Debian, Ubuntu, etc. homebrew by Mac OS X macports by Mac OS X yum/rpm by Redhat, CentOS, Fedora, etc. portage by Gentoo zypper by OpenSUSE SYNTAX $ pacapt OPERATIONS Query -Q list all installed packages -Qc show package's changelog -Qi print package status -Ql list package's files -Qm list installed packages that aren't available in any installation source -Qo query package that provides -Qp query a package file (don't use package database) Synchronize -S install package(s) -Ss search for packages -Su upgrade the system -Sy update package database -Suy update package database, then upgrade the system Remove / Clean up -R remove some packages -Sc delete old downloaded packages -Scc delete all downloaded packages -Sccc clean variant files. (debian) See more at http://dragula.org/blogs/646 OPTIONS -f force yes -v be verbose -w download packages but don't install them NOTES To install a package from backports repository on Debian system: $ pacapt -S foobar -t lenny-backports Similarly, any further option which isn't recognized by getopts (1) can be passed to system tool. When being executed on Arch-based system, the tool simply invokes the system tool '/usr/bin/pacman'. THANKS Special thanks to Châu An, Nguyễn (told me about 'Pacman Rosetta' [1]) Karol Blazewicz (for comments about '-Sy') James Pearson (for userful comments and patches. See also http://github.com/xiongchiamiov/pacapt) Alexander Dupuy (for userful comments and patches. See also https://github.com/dupuy/pacapt) Hà Dương, Nguyễn (for adding Gentoo support. See also https://github.com/CMPITG/pacapt) REFERENCES 1. Pacman Rosetta, https://wiki.archlinux.org/index.php?title=Pacman_Rosetta EOF } ### ### Helpers ### _os_is() { [[ "$_OSTYPE" = "$*" ]] } _exec_() { local _type="$1" shift if _os_is $_type; then [[ -z "$_VERBOSE" ]] || _error "Going to execute: $* $_VERBOSE $_FORCE" eval "$* $_VERBOSE $_FORCE" fi } # Detect package type from /etc/issue _found_arch() { local _ostype="$1" shift grep -qis "$*" /etc/issue && _OSTYPE="$_ostype" } # Detect package type _OSTYPE_detect() { _found_arch PACMAN "Arch Linux" && return _found_arch DPKG "Debian GNU/Linux" && return _found_arch DPKG "Ubuntu" && return _found_arch YUM "CentOS" && return _found_arch YUM "Red Hat" && return _found_arch YUM "Fedora" && return _found_arch ZYPPER "SUSE" && return [[ -z "$_OSTYPE" ]] || return # See also https://github.com/icy/pacapt/pull/22 # Please not that $OSTYPE (which is `linux-gnu` on Linux system) # is not our $_OSTYPE. The choice is not very good because # a typo can just break the logic of the program. if [[ "$OSTYPE" != "darwin"* ]]; then _error "Can't detect OS type from /etc/issue. Running fallback method." fi [[ -x "/usr/bin/pacman" ]] && _OSTYPE="PACMAN" && return [[ -x "/usr/bin/apt-get" ]] && _OSTYPE="DPKG" && return [[ -x "/usr/bin/yum" ]] && _OSTYPE="YUM" && return [[ -x "/opt/local/bin/port" ]] && _OSTYPE="MACPORTS" && return command -v brew >/dev/null && _OSTYPE="HOMEBREW" && return [[ -x "/usr/bin/emerge" ]] && _OSTYPE="PORTAGE" && return [[ -x "/usr/bin/zypper" ]] && _OSTYPE="ZYPPER" && return if [[ -z "$_OSTYPE" ]]; then _error "No supported package manager installed on system" _error "(supported: apt, homebrew, pacman, portage, yum)" exit 1 fi } ### ### Main ### # Detect type of package manager. _OSTYPE_detect # If the system is Arch-like, pass all of the arguments to /usr/bin/pacman # and return. This is done here to achieve a consistent help menu. [[ "$_OSTYPE" == "PACMAN" ]] && exec /usr/bin/pacman "$@" # # Get options from command lines. FIXME: Support long options # # By default, with Gentoo, 'force' is always 'yes', so we change it to 'no' [[ "$_OSTYPE" == "PORTAGE" ]] && _FORCE="-a" while getopts "URQShfvlumyispqwco" opt 2>/dev/null; do case "$opt" in Q|S|R|U) if [[ "$_POPT" != "" && "$_POPT" != "$opt" ]]; then _error "Only one operation may be used at a time" exit 1 fi _POPT="$opt" ;; # FIXME: Please check pacman(8) to see if they are really 2nd operation s|l|i|p|o|m) if [[ "$_SOPT" == '' ]]; then _SOPT="$opt" else _TOPT="$opt" fi ;; # NOTE: -q is an output option, not an operator! # Thanks to James Pearson for his catch :) q) _TOPT="$opt" ;; u) if [[ "${_SOPT:0:1}" = "y" ]]; then _SOPT="uy" else _SOPT="u" fi ;; y) if [[ "${_SOPT:0:1}" = "u" ]]; then _SOPT="uy" else _SOPT="y" fi ;; c) if [[ "${_SOPT:0:2}" = "cc" ]]; then _SOPT="ccc" elif [[ "${_SOPT:0:1}" = "c" ]]; then _SOPT="cc" else _SOPT="$opt" fi ;; w) case "$_OSTYPE" in "DPKG") _TOPT="-d" ;; "YUM") _TOPT="--downloadonly" if ! rpm -q yum-downloadonly > /dev/null; then _error "The '-w' option requires the package 'yum-downloadonly'" _error "Install this with 'yum install -y yum-downloadonly' or" _error "$0 -S -f yum-downloadonly" exit 1 fi ;; "MACPORTS") _TOPT="fetch" ;; "PORTAGE") _TOPT="--fetchonly" ;; "ZYPPER") _TOPT="--download-only" ;; esac ;; f) case "$_OSTYPE" in "DPKG") _FORCE="-f --force-yes" ;; "YUM") _FORCE="-y" ;; "MACPORTS") _FORCE="-f" ;; "PORTAGE") _FORCE="" ;; *) _FORCE="-y" ;; esac ;; v) _VERBOSE="-v" ;; h) _help; exit 0 ;; *) _error "Error: Invalid option"; exit 1 ;; esac done # Remained options/packages/queries shift $((OPTIND - 1)) _PKG="$*" # pacman man page (examples) says: "pacman -Syu gpm = Update package list, # upgrade all packages, and then install gpm if it wasn't already installed." # Instead, just disallow specific packages, as (ex-)yum users likely expect to # just update/upgrade one package (and its dependencies) and apt-get and pacman # have no way to do this. if [[ -n "$_PKG" ]]; then case "$_POPT$_SOPT" in "Su"|"Sy"|"Suy") _error "The -Sy/u options refresh and/or upgrade all packages" _error "To install packages as well, use separate commands to do so:" _error "$0 -S$_SOPT" _error "$0 -S $_PKG" exit 1 esac fi # DEBUG # echo "Primary options: $_POPT" # echo "Secondary options: $_SOPT" # echo "Options for operations: $_TOPT" # echo "Extra params: $_PKG" # echo "Verbose: $_VERBOSE" # echo "Force: $_FORCE" # Return if no option was specified if [[ -z "$_POPT" ]]; then _error "No operation specified (use -h for help)" exit 1 fi # Invoke the native package manager case "$_POPT$_SOPT" in #################################################################### # QUERYING # #################################################################### "Qi") _exec_ ZYPPER "zypper info $_PKG" _exec_ DPKG "dpkg-query -s $_PKG" _exec_ HOMEBREW "brew info $_PKG" #_exec_ MACPORTS "port info $_PKG" _os_is MACPORTS && _error "Function not implemented in macports" _exec_ YUM "yum info $_PKG" _FORCE="" \ _exec_ PORTAGE "emerge --info $_PKG" ;; "Ql") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _exec_ DPKG " if [[ -n \"$_PKG\" ]]; then dpkg-query -L $_PKG else dpkg -l \ | grep -E ^[hi]i \ | awk '{print \$2}' \ | while read _pkg; do if [[ \"$_TOPT\" = 'q' ]]; then dpkg-query -L \$_pkg else dpkg-query -L \$_pkg \ | while read _line; do echo \$_pkg \$_line done fi done fi " _exec_ HOMEBREW "brew list $_PKG" _exec_ MACPORTS "port contents $_PKG" _exec_ YUM "rpm -ql $_PKG" _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/qlist ]]; then qlist $_PKG elif [[ -x /usr/bin/equery ]]; then equery files $_PKG else _error 'You need to install portage-utils or gentoolkit to perform this operation.' fi " ;; "Qo") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _exec_ DPKG "dpkg-query -S $_PKG" _exec_ HOMEBREW " cd \"\$(dirname -- \$(which $_PKG))\" pkg=\"\$(pwd -P)/\$(basename -- $_PKG)\" prefix=\"\$(brew --prefix)\" cellar=\"\$(brew --cellar)\" for package in \$cellar/*; do files=(\${package}/*/\${pkg/#\$prefix\//}) if [[ -e \${files[\${#files[@]} - 1]} ]]; then echo \"\${package/#\$cellar\//}\" break fi done " _exec_ MACPORTS "port provides $_PKG" _exec_ YUM "rpm -qf $_PKG" _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/equery ]]; then equery belongs $_PKG else _error 'You need to install gentoolkit to perform this operation' fi " ;; "Qp") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _os_is HOMEBREW && _error "Function not implemented in homebrew" _os_is MACPORTS && _error "Function not implemented in macports" _os_is PORTAGE && _error "Function not supported as Gentoo has no definition of 'package file" _exec_ DPKG "dpkg-deb -I $_PKG" _exec_ YUM "rpm -qp $_PKG" ;; "Qc") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _os_is DPKG && _error "Function not implemented in Debian system" _exec_ HOMEBREW "brew log $_PKG" _exec_ MACPORTS "port log $_PKG" _exec_ YUM "rpm -q --changelog $_PKG" _FORCE="" \ _exec_ PORTAGE "emerge -p --changelog $_PKG" ;; "Qu") _exec_ ZYPPER "zypper list-updates" _exec_ DPKG "apt-get upgrade --trivial-only $_PKG" _exec_ HOMEBREW "brew outdated | grep $_PKG" _exec_ MACPORTS "port outdated $_PKG" _exec_ YUM "yum list updates $_PKG" _exec_ PORTAGE "emerge -uvN $_PKG" # FIXME: not exactly ;; "Qm") _exec_ ZYPPER "zypper search -si | grep 'System Packages'" _os_is DPKG && _error "Function not implemented in Debian system" _os_is HOMEBREW && _error "Function not implemented in homebrew" _os_is MACPORTS && _error "Function not implemented in macports" _os_is PORTAGE && _error "Function not supported in Gentoo" _exec_ YUM "yum list extras $_PKG" ;; "Q") _exec_ ZYPPER "zypper search -i" if [[ "$_TOPT" = 'q' ]]; then # FIXME: Should we redirect user to a similar operation? _os_is HOMEBREW && _error "Function not implemented in homebrew" _os_is MACPORTS && _error "Function not implemented in macports" _os_is PORTAGE && _error "Function not supported as Gentoo has no definition of 'package file" _os_is YUM && _error "Function not implemented in Yum" _exec_ DPKG "dpkg -l | grep -E ^[hi]i | awk '{print \$2}'" elif [[ "$_TOPT" = "" ]]; then _exec_ DPKG "dpkg -l $_PKG | grep -E ^[hi]i" _exec_ HOMEBREW "brew list | grep $_PKG" _exec_ MACPORTS "port installed $_PKG" _exec_ YUM "yum list installed $_PKG" # FIXME: There are actually many ways to do this in Gentoo _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/eix ]]; then eix -I $_PKG elif [[ -x /usr/bin/equery ]]; then equery list '*' $_PKG else LS_COLORS=never ls -1 -d /var/db/pkg/*/* fi " else _error "Error: Invalid option" exit 1 fi ;; #################################################################### # REMOVING # #################################################################### "Rs") if [[ "$_TOPT" = 's' ]]; then _os_is DPKG && _error "Function not implemented in Debian system" _os_is YUM && _error "Function not implemented in Yum" _os_is PORTAGE && _error "Function not supported in Gentoo" _exec_ ZYPPER "zypper remove $_PKG --clean-deps" _exec_ HOMEBREW " brew rm $_PKG brew rm \$(join <(brew leaves) <(brew deps $_PKG)) " _os_is MACPORTS && _error "Function not implemented in macports" elif [[ "$_TOPT" = '' ]]; then _exec_ ZYPPER "echo 'Function is not available'; exit 1" _os_is HOMEBREW && _error "Function not implemented in homebrew" _exec_ MACPORTS "port uninstall --follow-dependencies $_PKG" _exec_ DPKG "apt-get autoremove $_PKG" _exec_ YUM "yum erase $_PKG" _exec_ PORTAGE "emerge --depclean world $_PKG" else _error "Error: Invalid option" fi ;; "R") _exec_ ZYPPER "zypper remove $_PKG" _exec_ DPKG "apt-get remove $_PKG" _exec_ HOMEBREW "brew remove $_PKG" _exec_ MACPORTS "port uninstall $_PKG" _exec_ YUM "yum erase $_PKG" _exec_ PORTAGE "emerge --depclean $_PKG" ;; #################################################################### # SYNCHRONIZING # #################################################################### "Si") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _exec_ DPKG "dpkg-query -s $_PKG" _exec_ HOMEBREW "brew info $_PKG" _exec_ MACPORTS "port info $_PKG" _exec_ YUM "yum info $_PKG" _FORCE="" \ _exec_ PORTAGE "emerge --info $_PKG" ;; "Suy") _exec_ ZYPPER "zypper dup" _VERBOSE="" \ _exec_ DPKG "apt-get update; apt-get upgrade" _exec_ HOMEBREW "brew update; brew upgrade" _exec_ MACPORTS "port selfupdate; port upgrade outdated" _exec_ YUM "yum update" _exec_ PORTAGE " if [[ -x /usr/bin/layman ]]; then layman --sync-all \ && emerge --sync \ && emerge -auND world $_PKG else emerge --sync \ && emerge -uND world $_PKG fi " ;; "Su") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _VERBOSE="" \ _exec_ DPKG "apt-get upgrade" _exec_ HOMEBREW "brew upgrade" _exec_ MACPORTS "port upgrade outdated" _exec_ YUM "yum update" _exec_ PORTAGE "emerge -uND world $_PKG" ;; "Sy") _exec_ ZYPPER "zypper refresh" _VERBOSE="" \ _exec_ DPKG "apt-get update" _exec_ HOMEBREW "brew update" _exec_ MACPORTS "port selfupdate" # or sync? _exec_ YUM "yum check-update" _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/layman ]]; then layman --sync-all && emerge --sync else emerge --sync fi " ;; "Ss") _exec_ ZYPPER "zypper search $_PKG" _exec_ DPKG "apt-cache search $_PKG" _exec_ HOMEBREW "brew search $_PKG" _exec_ MACPORTS "port search $_PKG" _exec_ YUM "yum -C search $_PKG" _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/eix ]]; then eix $_PKG else emerge --search $_PKG fi " ;; "Sc") _exec_ ZYPPER "zypper clean" _exec_ DPKG "apt-get clean" _exec_ HOMEBREW "brew cleanup" _exec_ MACPORTS "port clean --all inactive" _exec_ YUM "yum clean expire-cache" __FORCE="" \ _exec_ PORTAGE " if [[ -x usr/bin/eclean-dist ]]; then eclean-dist -d -t1m -s50 -f else _error 'You need install gentoolkit to perform this operation.' fi " ;; "Scc") _exec_ ZYPPER "zypper clean" _exec_ DPKG "apt-get autoclean" _exec_ HOMEBREW "brew cleanup -s" _exec_ MACPORTS "port clean --all installed" _exec_ YUM "yum clean packages" _FORCE="" \ _exec_ PORTAGE " if [[ -x /usr/bin/eclean ]]; then eclean -i distfiles else _error 'You need install gentoolkit to perform this operation.' fi " ;; "Sccc") _exec_ ZYPPER "echo 'Function is not available'; exit 1" _exec_ DPKG "rm -fv /var/cache/apt/*.bin rm -fv /var/cache/apt/archives/*.* rm -fv /var/lib/apt/lists/*.* apt-get autoclean" _exec_ HOMEBREW 'rm -rf $(brew --cache)' _os_is MACPORTS && _error "Function not implemented in macports" _exec_ YUM "yum clean all" _FORCE="" \ _exec_ PORTAGE "rm -fv /usr/portage/distfiles/*.*" ;; "S") [[ -z "$_PKG" ]] \ && { _error "You must specify a package"; exit 1; } _exec_ ZYPPER "zypper install $_TOPT $_PKG" _exec_ DPKG "apt-get install $_TOPT $_PKG" _exec_ HOMEBREW "brew install $_TOPT $_PKG" if [[ "$_TOPT" = fetch ]] then _exec_ MACPORTS "port patch $_PKG" else _exec_ MACPORTS "port install $_TOPT $_PKG" fi _exec_ YUM "yum install $_TOPT $_PKG" _exec_ PORTAGE "emerge $_TOPT $_PKG" ;; #################################################################### # UPGRADING # #################################################################### "U") _exec_ ZYPPER "zypper install $_PKG" _os_is HOMEBREW && _error "Function not implemented in homebrew" _os_is MACPORTS && _error "Function not implemented in macports" _os_is PORTAGE && _error "You need to implement a local overlay and do the installation as usual." _exec_ DPKG "dpkg -i $_TOPT $_PKG" _exec_ YUM "yum localinstall $_TOPT $_PKG" ;; # Default option *) _error "Error: Invalid option" exit 1 ;; esac