Check if an apt-get package is installed and then install it if it's not on Linux

后端 未结 22 1145
轮回少年
轮回少年 2020-12-02 04:02

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)         


        
相关标签:
22条回答
  • 2020-12-02 04:10

    This seems to work pretty well.

    $ sudo dpkg-query -l | grep <some_package_name> | wc -l
    
    • It either returns 0 if not installed or some number > 0 if installed.
    0 讨论(0)
  • 2020-12-02 04:12

    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:

    • https://askubuntu.com/questions/165951/dpkg-get-selections-shows-packages-marked-deinstall
    • https://askubuntu.com/questions/423355/how-do-i-check-if-a-package-is-installed-on-my-server

    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?

    0 讨论(0)
  • 2020-12-02 04:12

    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...

    0 讨论(0)
  • 2020-12-02 04:12

    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.

    0 讨论(0)
  • 2020-12-02 04:14

    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
    
    0 讨论(0)
  • 2020-12-02 04:15

    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.

    0 讨论(0)
提交回复
热议问题