266

我正在使用 Ubuntu 系统,目前这就是我正在做的事情:

if ! which command > /dev/null; then
   echo -e "Command not found! Install? (y/n) \c"
   read
   if "$REPLY" = "y"; then
      sudo apt-get install command
   fi
fi

这是大多数人会做的吗?还是有更优雅的解决方案?

4

25 回答 25

351

要检查是否packagename已安装,请键入:

dpkg -s <packagename>

您还可以使用dpkg-query具有更简洁的输出来满足您的目的,并且也可以接受通配符。

dpkg-query -l <packagename>

要查找哪个包拥有command,请尝试:

dpkg -S `which <command>`

有关详细信息,请参阅文章Find if package is installed in Linux and dpkg cheat sheet

于 2009-08-19T06:27:47.443 回答
108

更明确地说,这里有一个 Bash 脚本,它检查一个包并在需要时安装它。当然,您可以在发现包丢失时做其他事情,例如简单地退出并显示错误代码。

REQUIRED_PKG="some-package"
PKG_OK=$(dpkg-query -W --showformat='${Status}\n' $REQUIRED_PKG|grep "install ok installed")
echo Checking for $REQUIRED_PKG: $PKG_OK
if [ "" = "$PKG_OK" ]; then
  echo "No $REQUIRED_PKG. Setting up $REQUIRED_PKG."
  sudo apt-get --yes install $REQUIRED_PKG
fi

如果脚本在 GUI 中运行(例如,它是Nautilus脚本),您可能希望将 'sudo' 调用替换为 'gksudo' 调用。

于 2012-05-03T20:44:36.947 回答
90

这个单线返回 1(已安装)或 0(未安装)“nano”包...

$(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed")

即使包不存在或不可用。

如果未安装,下面的示例将安装“nano”包...

if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
  apt-get install nano;
fi
于 2014-03-23T15:22:03.830 回答
29

dpkg-query --showformat='${db:Status-Status}'

这会产生一个小的输出字符串,该字符串不太可能改变,并且很容易在没有 的情况下进行确定性比较grep

pkg=hello
status="$(dpkg-query -W --showformat='${db:Status-Status}' "$pkg" 2>&1)"
if [ ! $? = 0 ] || [ ! "$status" = installed ]; then
  sudo apt install $pkg
fi

需要进行检查,$? = 0因为如果您之前从未安装过软件包,并且在删除某些软件包(例如 )之后hellodpkg-query将以状态 1 退出并输出到 stderr:

dpkg-query: no packages found matching hello

而不是输出not-installed. 当2>&1它阻止它进入终端时,它也会捕获该错误消息。

对于多个包:

pkgs='hello certbot'
install=false
for pkg in $pkgs; do
  status="$(dpkg-query -W --showformat='${db:Status-Status}' "$pkg" 2>&1)"
  if [ ! $? = 0 ] || [ ! "$status" = installed ]; then
    install=true
    break
  fi
done
if "$install"; then
  sudo apt install $pkgs
fi

可能的状态记录man dpkg-query如下:

   n = Not-installed
   c = Config-files
   H = Half-installed
   U = Unpacked
   F = Half-configured
   W = Triggers-awaiting
   t = Triggers-pending
   i = Installed

单字母版本可通过 获得db:Status-Abbrev,但它们与操作和错误状态一起出现,因此您获得 3 个字符,需要将其剪切。

所以我认为依靠非大写状态(Config-filesvs config-files)而不改变是足够可靠的。

dpkg -s退出状态

不幸的是,这并不能满足大多数用户的需求:

pkgs='qemu-user pandoc'
if ! dpkg -s $pkgs >/dev/null 2>&1; then
  sudo apt-get install $pkgs
fi

因为对于某些软件包,例如certbot,正在执行:

sudo apt install certbot
sudo apt remove certbot

leave certbotin state config-files,这意味着配置文件留在了机器中。在这种状态下,dpkg -s仍然返回0,因为包元数据仍然保留,以便可以更好地处理这些配置文件。

要真正dpkg -s根据需要返回 1,--purge需要:

sudo apt remove --purge certbot

这实际上将其移入not-installed/ dpkg-query: no packages found matching

请注意,只有某些软件包会留下配置文件。一个更简单的包,如hello直接从无installednot-installed--purge

在 Ubuntu 20.10 上测试。

Pythonapt

在 Ubuntu 18.04 中有一个预安装的 Python 3 包apt,它公开了一个 Python apt 接口!

可以在以下位置查看检查是否安装了包并安装它的脚本:如何使用 python-apt API 安装包

这是一份供参考的副本:

#!/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))

检查是否有可执行PATH文件

请参阅:如何从 Bash 脚本中检查程序是否存在?

也可以看看

于 2019-01-17T15:49:17.073 回答
12

Ubuntu 添加了它的“Personal Package Archive”(PPA),而 PPA 包有不同的结果。

  1. 未安装本机Debian存储库包:

    ~$ dpkg-query -l apache-perl
    ~$ echo $?
    1
    
  2. 在主机上注册并安装的 PPA 包:

    ~$ dpkg-query -l libreoffice
    ~$ echo $?
    0
    
  3. 在主机上注册但未安装的 PPA 包:

    ~$ dpkg-query -l domy-ce
    ~$ echo $?
    0
    ~$ sudo apt-get remove domy-ce
    [sudo] password for user:
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    Package domy-ce is not installed, so not removed
    0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
    

还发布在:测试是否在 APT 中安装了一个包

于 2012-05-23T13:12:11.643 回答
7

UpAndAdam 写道

但是,您不能简单地依靠此处的返回码来编写脚本

根据我的经验,您可以依赖 dkpg 的退出代码。

dpkg -s的返回码是0如果安装了包,如果没有安装,则返回1,所以我找到的最简单的解决方案是:

dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>

这对我来说可以...

于 2014-09-22T13:02:16.043 回答
6

这似乎工作得很好。

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • 如果未安装,则返回,如果已安装,则返回0某个数字> 0
于 2013-08-20T17:52:47.813 回答
6

我已经根据Nultyi 的回答确定了一个:

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

基本上,来自的错误消息dpkg --get-selections比大多数其他错误消息更容易解析,因为它不包括“卸载”之类的状态。它还可以同时检查多个包,而仅使用错误代码是无法做到的。

说明/示例:

$ 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

因此grep从列表中删除已安装的软件包,并awk从错误消息中提取软件包名称,从而生成MISSING='python3-venv python3-dev jq',可以将其轻松插入到安装命令中。

我不是盲目地发布apt-get install $PACKAGES,因为正如评论中所提到的,这可能会意外地升级您不打算使用的软件包;对于预期稳定的自动化流程来说,这并不是一个好主意。

于 2018-02-05T04:15:01.953 回答
5

现在似乎apt-get有一个选项--no-upgrade可以满足 OP 的要求:

--no-upgrade不要升级软件包。与 install 一起使用时,no-upgrade 将阻止列出的软件包在已安装的情况下升级。

来自https://linux.die.net/man/8/apt-get的手册页

因此你可以使用

apt-get install --no-upgrade package

并且package只有在没有安装时才会安装。

于 2019-05-06T14:27:29.333 回答
4

这会做到的。apt-get install是幂等的。

sudo apt-get install --no-upgrade command
于 2016-04-05T00:00:16.383 回答
4

我发现以前的答案中的所有解决方案都可能会产生误报,如果安装包然后删除,但安装包仍保留在系统上。

要复制:

安装包apt-get install curl
删除包apt-get remove curl

现在测试以前的答案。

以下命令似乎可以解决这种情况:

dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

这将导致最终安装或未安装

于 2016-05-03T14:48:00.187 回答
3
$name="rsync"

[ `which $name` ] $$ echo "$name : installed" || sudo apt-get install -y $name
于 2015-09-11T14:58:08.547 回答
3

采用:

apt-cache policy <package_name>

如果没有安装,会显示:

Installed: none

否则会显示:

Installed: version
于 2017-07-14T18:59:58.990 回答
3

克里斯回答的启发:

#! /bin/bash

installed() {
    return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}

pkgs=(libgl1-mesa-dev xorg-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools)
missing_pkgs=""

for pkg in ${pkgs[@]}; do
    if ! $(installed $pkg) ; then
        missing_pkgs+=" $pkg"
    fi
done

if [ ! -z "$missing_pkgs" ]; then
    cmd="sudo apt install -y $missing_pkgs"
    echo $cmd
fi
于 2021-02-19T04:49:37.083 回答
1

此功能已存在于 Ubuntu 和 Debian 的command-not-found软件包中。

于 2009-08-19T09:15:52.567 回答
0

这个命令是最难忘的:

dpkg --get-selections <package-name>

如果已安装,则会打印:

<包名> 安装

否则会打印

找不到与 <package-name> 匹配的包。

这是在 Ubuntu 12.04.1 (Precise Pangolin) 上测试的。

于 2013-09-25T09:38:01.220 回答
0
which <command>
if [ $? == 1 ]; then
    <pkg-manager> -y install <command>
fi
于 2017-03-19T10:48:56.073 回答
0
apt list [packagename]

似乎是 dpkg 和较旧的apt -* 工具之外最简单的方法。

于 2017-07-17T23:31:22.283 回答
0

对于 Ubuntu,apt提供了一种相当不错的方法来做到这一点。以下是谷歌浏览器的示例:

apt -qq list google-chrome-stable 2>/dev/null | grep -qE "(installed|upgradeable)" || apt-get install google-chrome-stable

我将错误输出重定向到 null,因为apt警告不要使用其“不稳定的 cli”。我怀疑 list 包是稳定的,所以我认为可以丢弃这个警告。-qq 使 apt 超级安静。

于 2017-12-06T18:45:56.227 回答
0

在本地而不是在 Docker 中运行测试时,我有类似的要求。基本上我只想安装任何尚未安装的 .deb 文件。

# 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

我想我能看到的唯一问题是它不检查包的版本号,所以如果 .deb 文件是较新的版本。那么这不会覆盖当前安装的包。

于 2018-08-21T17:48:01.940 回答
0

如果仅使用 awk 安装 else 1,则会显式打印 0:

dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 0;exit}{print 1}'

或者,如果您更喜欢 1 表示已安装,否则为 0:

dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 1;exit}{print 0}'

** 用你的包名替换 PKG

便利功能:

installed() {
    return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}


# usage:
installed gcc && echo Yes || echo No

#or

if installed gcc; then
    echo yes
else
    echo no
fi
于 2020-12-02T20:01:07.180 回答
0

有点基于你的,只是更“优雅”了一点。只是因为我很无聊。

#!/bin/bash
FOUND=("\033[38;5;10m")
NOTFOUND=("\033[38;5;9m")
PKG="${@:1}"
command ${PKG} &>/dev/null
if [[ $? != 0 ]]; then
    echo -e "${NOTFOUND}[!] ${PKG} not found [!]"
    echo -e "${NOTFOUND}[!] Would you like to install ${PKG} now ? [!]"
    read -p "[Y/N] >$ " ANSWER
    if [[ ${ANSWER} == [yY] || ${ANSWER} == [yY][eE][sS] ]]; then
        if grep -q "bian" /etc/os-release; then
            sudo apt-get install ${PKG}
        elif grep -q "arch" /etc/os-release; then
            if [[ -f /bin/yay ]] || [[ -f /bin/yaourt ]]; then
                yaourt -S ${PKG} 2>./err || yay -S ${PKG} 2>./err
            else
                sudo pacman -S ${PKG}
            fi
        elif grep -q "fedora" /etc/os-release; then
             sudo dnf install ${PKG}
        else
            echo -e "${NOTFOUND}[!] This script couldn't detect your package manager [!]"
            echo -e "${NOTFOUND}[!] Manually install it [!]"
        fi
    elif [[ ${ANSWER} == [nN] || ${ANSWER} == [nN][oO] ]]; then
        echo -e "${NOTFOUND}[!] Exiting [!]"
    fi
else
    echo -e "${FOUND}[+] ${PKG} found [+]"
fi
于 2021-12-14T02:10:58.637 回答
0

建议使用以下内容的答案:

dpkg-query --showformat '${db:Status-Status}\n' --show $package | grep -q '^installed$'
dpkg-query --showformat '${Status}\n' --show $package | grep -q '^install ok installed$'

是正确的。

但是,如果您安装了软件包dpkg-dev,并且您不仅想检查是否安装了软件包,而且还:

  • 想知道某个包是否安装在某个版本
  • 想要在某个架构中拥有一个包
  • 想看看有没有提供虚拟包

那么您可以滥用该dpkg-checkbuilddeps工具来完成这项工作:

dpkg-checkbuilddeps -d apt /dev/null

这将检查是否安装了apt。

下面将检查 apt 是否安装在至少 2.3.15 版本中并且 grep 是否安装为 amd64 以及虚拟包x-window-manager是否由一些已安装的包提供:

dpkg-checkbuilddeps -d 'apt (>= 2.3.15), grep:amd64, x-window-manager' /dev/null

dpkg-checkbuilddeps 的退出状态会告诉脚本是否满足依赖关系。dpkg-checkbuilddeps由于此方法支持传递多个包,因此即使要检查是否安装了多个包,也只需运行一次。

于 2022-02-15T09:51:49.783 回答
-1

我使用以下方式:

which mySQL 2>&1|tee 1> /dev/null
  if [[ "$?" == 0 ]]; then
                echo -e "\e[42m MySQL already installed. Moving on...\e[0m"
        else
        sudo apt-get install -y mysql-server
                if [[ "$?" == 0 ]]; then
                        echo -e "\e[42mMy SQL installed\e[0m"
                else
                        echo -e "\e[42Installation failed\e[0m"
                fi
        fi
于 2019-02-06T13:29:06.287 回答
-1

我使用这个解决方案,因为我发现它最简单。

function must_install(){
   return "$(apt -qq list $var --installed 2> /dev/null |wc -l)"
}

function install_if() {
    unset install
    for var in "$@"
    do
        if $(must_install $var)
        then
            install+="${var} "
        fi
    done
    if [ -n "$install" ];
    then
        sudo apt-get install -qy $install
    fi
}

巧妙的是,must_install返回 1 或 0,然后从调用中将其解释为 true 或 false if,因此我们不需要任何testusing []

install_if接受由空格分隔的任意数量的包。

该问题apt不打算在脚本中使用,因此这可能随时停止工作。8)

于 2021-03-12T10:17:36.510 回答