检查是否安装了apt-get软件包,如果不在Linux上,请安装它


223

我正在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

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


7
命令名称并不总是反映它们所属的程序包名称。您的最大目标是什么?您为什么不简单地尝试安装它,而最坏的情况却不会,因为它已经安装了。
viam0Zah

8
幸运的是,apt-get install是幂等的,因此仅运行它就很安全,不必担心它是否已安装。
David Baucum '16

@DavidBaucum的评论应该是获得最多投票的答案。
Nirmal

@Nirmal,已回答。
David Baucum 2016年

1
相关的,你应该使用command -v <command>; 不which <command>。另请参阅检查Bash脚本中是否存在程序
jww

Answers:


314

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

dpkg -s <packagename>

您也可以使用dpkg-query具有整洁输出的输出,并接受通配符。

dpkg-query -l <packagename>

要查找拥有的软件包command,请尝试:

dpkg -S `which <command>`

有关更多详细信息,请参阅文章查找软件包是否已在Linuxdpkg备忘单中安装


31
如果您作为一个人以非编程方式想要此信息,则可以使用此信息。但是,您不能仅仅依靠返回码来编写脚本,也不能仅仅依靠输出或输出的不足来编写脚本。您将不得不扫描这些命令的输出,从而限制了它们在此问题上的有用性。
UpAndAdam

4
奇怪的是,我最近发现dpkg-query曾经在缺少的软件包上返回1,而现在(Ubuntu 12.04)返回0,这给我的jenkins构建节点设置脚本带来了各种麻烦!dpkg -s在安装的软件包上返回0,在未安装的软件包上返回1。
Therealstubot

17
嘿,OP要求if使用。我也在寻找if用法。
托马什Zato -恢复莫妮卡

1
@Therealstubot:我也在使用Ubuntu 12.04,dpkg -s并且在缺少的软件包上确实返回1,否则返回0。早期(或最新)版本有何不同?
MestreLion

4
注意: dpkg -s如果安装了某个软件包然后将其删除,则返回零-在这种情况下,它是Status: deinstall ok config-files或类似的,所以是“好的”-因此对我来说,这不是一个安全的测试。 dpkg-query -l在这种情况下,似乎也没有返回有用的结果。
敏锐

86

为了更加明确,这里有一些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”调用。


5
--force-yes似乎是个坏主意。来自手册页:“这是一个危险的选项,它将导致apt-get继续运行而不会提示是否正在做潜在有害的事情。除非在非常特殊的情况下,否则不应使用它。使用--force-yes可能会破坏您的系统!!” 在脚本中使用它会使情况变得更糟。
减少活动

68

这种单行返回'nano'软件包的1(已安装)或0(未安装)。

$(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

4
我对此的变化:dpkg-query -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
ThorSummoner 2015年

@ThorSummoner:关心解释为什么你的更好?
knocte '16

1
@knocte我不确定是否有关于客观上变得更好的论点。尽管我有信心逐字记录的单行代码将执行结果输出,但我不想在回答中犹豫不决。我展示的一个衬纸说明仅获取(打印)退出代码。
ThorSummoner '16

1
@ThorSummoner您不需要grep -P像这样的简单正则表达式。
三胞胎

7
更简单: if ! dpkg-query -W -f='${Status}' nano | grep "ok installed"; then apt install nano; fi-无需使用grep -c,只需使用“ grep
斯蒂芬·奥斯特米勒

17

dpkg -s 自动安装以编程方式使用

我很喜欢,dpkg -s因为1如果没有安装任何软件包,它会以状态退出,因此易于自动化:

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

man dpkg 不幸的是没有记录退出状态,但是我认为依靠它应该是相当安全的:

-s, --status package-name...
    Report status of specified package.

要注意的一件事是运行:

sudo apt remove <package-name>

不一定会立即删除某些程序包的所有文件(但对于其他程序包会立即删除,不确定为什么吗?),而只是将程序包标记为要删除。

在这种状态下,该软件包似乎仍然可以使用,并且其文件仍然存在,但是以后将其标记为删除。

例如,如果您运行:

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

然后:

  • 前两个echo $?输出0,只有第三个输出1

  • 第一个的输出dpkg -s certbot包含:

    Status: deinstall ok installed

    而第二个说:

    Status: deinstall ok config-files

    并且仅在清除后消失:

    dpkg-query: package 'certbot' is not installed and no information is available
  • 该文件/etc/logrotate.d/certbot在系统中仍然存在apt remove,但之后没有--purge

    但是,/usr/lib/python3/dist-packages/certbot/reporter.py即使在之后,该文件仍然存在--purge

我不明白为什么,但与hello包第二dpkgapt remove表明,他包已经被删除无--purge

dpkg-query: package 'hello' is not installed and no information is available

文档也非常不清楚,例如:

sudo apt dselect-upgrade

certbot标记为时并未删除deinstall,即使man apt-get似乎表明:

dselect-upgrade与传统的Debian打包前端dselect(1)结合使用。dselect-upgrade遵循dselect(1)对可用软件包的“状态”字段所做的更改,并执行实现该状态所需的操作(例如,删除旧软件包和安装新软件包)。

也可以看看:

在Ubuntu 19.10上测试。

Python apt

apt在Ubuntu 18.04中有一个预安装的Python 3软件包,它公开了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脚本中是否存在程序?


Ciro,您不能依赖“ dpkg -s”退出代码。例如,如果您“ apt已安装”软件包,然后“ apt删除”了该软件包并尝试“ dpkg -s packagename”,则您将注意到状态:卸载并退出代码零(就像已安装)。您必须解析“ dpkg -s”输出兄弟。
德米特里·谢夫科普利亚斯

@DmitryShevkoplyas感谢您的报告。我无法在Ubuntu 19.10与重现:sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?。您能否提供更多详细信息?
西罗Santilli郝海东冠状病六四事件法轮功

1
确实-对于带有“ hello”软件包的测试用例,“ dpkg -s”软件包正确显示该软件包未安装,并给出了预期的退出代码“ 1”。但是,尝试对软件包“ certbot”进行相同的安装/删除检查,然后在“ apt remove certbot”显示退出后,状态为“ dpkg -s”的状态将显示“状态:卸载ok config-files”,并且退出代码错误地向我们显示“ 0”。我的错误假设是,对于任何其他软件包来说都是正确的情况,但是似乎对于所有软件包来说都不一样,这甚至更糟,更难以预测。解析“ dpkg -s”,您必须(c)Yoda :)
德米特里Shevkoplyas

11

我提供了此更新,因为Ubuntu刚刚添加了其“个人软件包存档”(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.

也发布在:https : //superuser.com/questions/427318/test-if-a-package-is-installed-in-apt/427898


2
如果安装和删除软件包,则接下来使用dpkg-query package; 回声$?如果未安装该软件包,也将为0。
Pol Hallen

8

UpAndAdam写道:

但是,您不能仅仅依靠返回码来编写脚本

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

如果安装了软件包,则dpkg -s的返回码为0,否则为1,因此我找到的最简单的解决方案是:

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

对我来说很好...


11
之后apt-get remove <package>dpkg -s <package>即使软件包为deinstalled
ThorSummoner

7

这似乎工作得很好。

$ sudo dpkg-query -l | grep <some_package_name> | wc -l
  • 0如果未安装,则返回;如果已安装,则返回一些> 0

8
grep | wc -l是反模式。要检查是否存在某些东西,您只需要grep -q。要实际计算发生次数(在这种情况下很少有用),请使用grep -c
Tripleee '16

@tripleee那么,dpkg -s zip | grep -c "Package: zip"?(使用zip作为示例包)
David Tabernero M.18年

@Davdriver那不是上面所做的,但是是的。在脚本中,您可能想grep -q 'Package: zip'返回一个退出代码,该退出代码指示是否在不打印任何内容的情况下找到了结果。
Tripleee '18

这似乎也适用于已卸载的软件包
mehmet

4

我已经根据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比其他大多数错误消息都容易解析,因为它不包含“ deinstall”之类的状态。它还可以同时检查多个程序包,而仅仅使用错误代码是无法做到的。

说明/示例:

$ 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因为正如评论中提到的那样,这可能会意外升级您不打算使用的软件包。对于预期稳定的自动化过程而言,这并不是一个好主意。


我喜欢这个解决方案。一次简练和测试多个包裹。此外,您还可以进行可选的检查,例如[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
Shenk

3

我发现,如果安装了软件包然后再将其删除,但所有安装解决方案仍保留在系统上,上述所有解决方案都可能产生误报。

复制:安装软件包apt-get install curl
删除软件包apt-get remove curl

现在测试以上答案。

以下命令似乎可以解决此问题:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'

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


遗憾的是,这可能不是完全不完全的,在这种情况下,还有其他可能的结果,config-files因此,我认为| grep -q "installed"确实需要最终输入才能获得功能退出代码状态。
敏锐

做到这一点| grep -q '^installed$'
敏锐的

3

看来,现在apt-get有一个选项--no-upgrade,只是做了OP想要的东西:

--no-upgrade不要升级软件包。与安装结合使用时,如果已经安装了列出的软件包,则不进行升级将阻止对其进行升级。

来自https://linux.die.net/man/8/apt-get的联机帮助页

因此,您可以使用

apt-get install --no-upgrade package

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



2

这样就可以了。apt-get install是幂等的。

sudo apt-get install command

5
在某些情况下apt-get install,即使命令本身是幂等的,也不希望在已经安装了软件包的情况下对软件包执行操作。就我而言,我正在使用Ansible的raw模块在远程系统上安装一个软件包,如果我apt-get install不加选择地运行,每次都会报告系统已更改。有条件的解决了这个问题。
JBentley '16

1
@JBentley这是一个好点。作为依赖项的一部分安装的软件包将被标记为手动安装,如果您通过apt-get install来删除依赖项,则不会将其删除。
David Baucum '16

2

用:

apt-cache policy <package_name>

如果未安装,它将显示:

Installed: none

否则它将显示:

Installed: version

1

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


15
matt @ matt-ubuntu:〜$命令未找到命令未找到:命令未找到...大声笑。
Matt Fletcher

1
command-not-found是一个交互式帮助程序,不是确保您具有所需依赖项的工具。当然,声明依赖关系的正确方法是将软件打包在Debian软件包中,并Depends:在软件包debian/control文件中正确填写声明。
Tripleee '16

1
apt list [packagename]

似乎是在dpkg和较旧的apt- *工具之外执行此操作的最简单方法


手动检查很不错,但是会发出警告,告知apt不适合脚本编写-与apt- *工具相反。
Hontvári利文特


1

在本地而不是在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文件是较新的版本,则不会覆盖当前安装的软件包。


1

对于Ubuntu,apt提供了一种相当不错的方法。以下是Google chrome的示例:

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

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


1
如果某些东西是“可升级的”,这将无法正常工作
Pawel Barcik

@PawelBarcik好点。我已经更新了答案来处理这种情况。
carlin.scott

0

此命令最难忘:

dpkg --get-selections <package-name>

如果已安装,它将打印:

<package-name>安装

否则会打印

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

这已在Ubuntu 12.04.1(精确的穿山甲)上进行了测试。


4
dpkg --get-selections <package-name>找不到包时,不会将退出代码设置为非零。
卢卡斯2014年

0

已经讲了很多事情,但对我而言,最简单的方法是:

dpkg -l | grep packagename

0

在Bash中:

PKG="emacs"
dpkg-query -l $PKG > /dev/null || sudo apt install $PKG

请注意,您可以在一个PKG中包含多个包的字符串。


0

我使用以下方式:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.