I\'m working on a Ubuntu system and currently this is what I\'m doing:
if ! which command > /dev/null; then
echo -e \"Command not found! Install? (y/n)
This seems to work pretty well.
$ sudo dpkg-query -l | grep <some_package_name> | wc -l
0
if not installed or some number > 0
if installed.dpkg -s
programmatic usage with automatic install
I like dpkg -s
as it exits with status 1
if any of the packages is not installed, making it easy to automate:
pkgs='qemu-user pandoc'
if ! dpkg -s $pkgs >/dev/null 2>&1; then
sudo apt-get install $pkgs
fi
man dpkg
does not document the exit status unfortunately, but I think it should be reasonably safe to rely on it:
-s, --status package-name...
Report status of specified package.
One thing to note is that running:
sudo apt remove <package-name>
does not necessarily remove all files immediately for some packages (but does for others, not sure why?), and just marks the package for removal.
In this state, the package appears to be still usable, and as its files are still present, but it is marked for removal later on.
For example if you run:
pkg=certbot
sudo apt install -y "$pkg"
dpkg -s "$pkg"
echo $?
sudo apt remove -y "$pkg"
dpkg -s "$pkg"
echo $?
ls -l /usr/lib/python3/dist-packages/certbot/reporter.py
sudo apt remove --purge certbot
dpkg -s "$pkg"
echo $?
ls -l /usr/lib/python3/dist-packages/certbot/reporter.py
then:
the first two echo $?
output 0
, only the third one outputs 1
the output for the first dpkg -s certbot
contains:
Status: deinstall ok installed
while the second says:
Status: deinstall ok config-files
and it only disappears after purge:
dpkg-query: package 'certbot' is not installed and no information is available
the file /etc/logrotate.d/certbot
is still present in the system after apt remove
, but not after --purge
.
However, the file /usr/lib/python3/dist-packages/certbot/reporter.py
is still present even after --purge
.
I don't understand why, but with the hello
package the second dpkg
after apt remove
shows that he package has already been removed without --purge
:
dpkg-query: package 'hello' is not installed and no information is available
The documentations are also very unclear, e.g.:
sudo apt dselect-upgrade
did not remove certbot
when it was marked as deinstall
, even though man apt-get
seems to indicate that:
dselect-upgrade
is used in conjunction with the traditional Debian packaging front-end, dselect(1). dselect-upgrade follows the changes made by dselect(1) to the Status field of available packages, and performs the actions necessary to realize that state (for instance, the removal of old and the installation of new packages).
See also:
Tested on Ubuntu 19.10.
Python apt
package
There is a pre-installed Python 3 package called apt
in Ubuntu 18.04 which exposes an Python apt interface!
A script that checks if a package is installed and installs it if not can be seen at: How to install a package using the python-apt API
Here is a copy for reference:
#!/usr/bin/env python
# aptinstall.py
import apt
import sys
pkg_name = "libjs-yui-doc"
cache = apt.cache.Cache()
cache.update()
cache.open()
pkg = cache[pkg_name]
if pkg.is_installed:
print "{pkg_name} already installed".format(pkg_name=pkg_name)
else:
pkg.mark_install()
try:
cache.commit()
except Exception, arg:
print >> sys.stderr, "Sorry, package installation failed [{err}]".format(err=str(arg))
Check if an executable is in PATH
instead
See: How can I check if a program exists from a Bash script?
UpAndAdam wrote:
However you can't simply rely on return codes here for scripting
In my experience you can rely on dkpg's exit codes.
The return code of dpkg -s is 0 if the package is installed and 1 if it's not, so the simplest solution I found was:
dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>
Works fine for me...
I had a similar requirement when running test locally instead of in docker. Basically I only wanted to install any .deb files found if they weren't already installed.
# If there are .deb files in the folder, then install them
if [ `ls -1 *.deb 2> /dev/null | wc -l` -gt 0 ]; then
for file in *.deb; do
# Only install if not already installed (non-zero exit code)
dpkg -I ${file} | grep Package: | sed -r 's/ Package:\s+(.*)/\1/g' | xargs dpkg -s
if [ $? != 0 ]; then
dpkg -i ${file}
fi;
done;
else
err "No .deb files found in '$PWD'"
fi
I guess they only problem I can see is that it doesn't check the version number of the package so if .deb file is a newer version, then this wouldn't overwrite the currently installed package.
This one-liner returns 1 (installed) or 0 (not installed) for the 'nano' package..
$(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed")
even if the package does not exist / is not available.
The example below installs the 'nano' package if it is not installed..
if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
apt-get install nano;
fi
I've settled on one based on Nultyi's answer:
MISSING=$(dpkg --get-selections $PACKAGES 2>&1 | grep -v 'install$' | awk '{ print $6 }')
# Optional check here to skip bothering with apt-get if $MISSING is empty
sudo apt-get install $MISSING
Basically, the error message from dpkg --get-selections
is far easier to parse than most of the others, because it doesn't include statuses like "deinstall". It also can check multiple packages simultaneously, something you can't do with just error codes.
Explanation/example:
$ dpkg --get-selections python3-venv python3-dev screen build-essential jq
dpkg: no packages found matching python3-venv
dpkg: no packages found matching python3-dev
screen install
build-essential install
dpkg: no packages found matching jq
So grep removes installed packages from the list, and awk pulls the package names out from the error message, resulting in MISSING='python3-venv python3-dev jq'
, which can be trivially inserted into an install command.
I'm not blindly issuing an apt-get install $PACKAGES
because as mentioned in the comments, this can unexpectedly upgrade packages you weren't planning on; not really a good idea for automated processes that are expected to be stable.