如何检查一个软件包是否已安装(无超级用户权限)?

在我们的大学,我们几乎可以安装任何我们想要的Ubuntu软件包,但我们自己不是超级用户(我们需要请求安装软件包)。
对于某些库,很难确定软件包是否已经安装。有没有一种简单的方法/命令来检查这个?
9个回答

apt-cache policy <package name>

8重要的是要知道它支持在包名中进行制表符补全。因此,即使您不知道确切的包名,它也非常有用。 - Javier Rivera
1可惜的是,即使软件包不存在,状态仍然显示为0。但是dpkg -s命令可以提供更多信息,详见:https://askubuntu.com/a/1108801/52975 - Ciro Santilli OurBigBook.com
使用更友好的 apt 命令:apt policy <软件包名称>apt show <软件包名称> 以获取更多信息。 - Pablo Bianchi

我通常只是在命令行中使用这个。
dpkg -l | grep mysql

所以上面的命令要求dpkg列出所有已安装的软件包,然后我使用grep筛选出只有mysql名称的软件包。

3dpkg -l "*mysql*" 也可以工作,并且不会隐藏输出中的解释性行。 - Philipp Wendler
dpkg -l "*mysql*"(或 dpkg -l '*mysql*')还可以对输出进行特殊格式化,以适应终端窗口中每个软件包的一行显示(grep不能做到)。而且这种形式允许您选择通配符的格式(前缀或后缀),以及是否使用通配符(与grep相比)。但是grep提供了漂亮的着色效果.. )) - vstepaniuk
这是不正确的。dpkg -l 不会要求 dpkg 列出所有已安装的软件包。dpkg -l 的输出还会包括你安装但后来删除的软件包。正确的方法是通过检查 dpkg -l 输出的第一列是否等于 ii 或者运行:dpkg-query --showformat '${Status}\n' --show $pkg,它将对实际安装的软件包显示 install ok installed - josch

另一种变体,这次使用才能:
aptitude show <package>

Tab补全在这里也适用。

你可以使用dselect。它提供非su只读访问权限。
另外,dpkg -s <软件包名称>提供了与软件包相关的许多详细信息。例如"
userme:~$ dpkg-query -s sl
Package: sl
Status: unknown ok not-installed
Priority: optional
Section: games

1这也可以只用 dpkg -s 来实现。反之,dpkg-query -l 同样可以像 dpkg -ldpkg --list 一样正常工作。 - belacqua

在脚本中,您可以使用dpkg-query -s <package> 2>/dev/null | grep -q ^"Status: install ok installed"$命令,如果<package>未安装,则返回退出码1,如果<package>已安装,则返回退出码0。

5小心:如果dpkg -s返回0,并不一定意味着软件包已经完全/正确地安装。如果软件包处于half-configured或者config-files状态,dpkg -s同样会返回0(我猜在half-installed状态下也是如此,但我没有检查过)。请参考dpkg的手册页面了解更多关于"不完整"状态的信息。 - Ignitor
1@Ignitor,说得好。我的回答是错误的。这个软件包甚至可以被移除,但不能被完全清除。所以我认为你需要检查输出来确定软件包是否已安装。 - jarno
2我编辑了答案。现在它依赖于 dpkg-query 的输出。我不知道这个解决方案有多么通用;例如,在某些系统中,文本可能显示为另一种语言吗? - jarno

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检查,因为如果您以前从未安装过软件包,并且在删除某些软件包(例如hello)之后,dpkg-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-files vs 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

certbot保留在“config-files”状态,这意味着配置文件仍然留在机器上。在这种状态下,dpkg -s仍然返回0,因为软件包元数据仍然保留,以便更好地处理这些配置文件。

要实际使dpkg -s返回所需的1,需要使用--purge

sudo apt remove --purge certbot

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

请注意,只有某些软件包会留下配置文件。像hello这样的简单软件包会直接从installed转到not-installed,而无需使用--purge

在Ubuntu 20.10上进行了测试。

Python apt软件包

在Ubuntu 18.04中有一个预安装的Python 3软件包,名为apt,它提供了一个Python apt接口!

可以在以下链接中找到一个检查软件包是否已安装并在未安装时安装它的脚本:https://stackoverflow.com/questions/17537390/how-to-install-a-package-using-the-python-apt-api/17538002#17538002

以下是供参考的副本:

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

参见:https://stackoverflow.com/questions/592620/how-can-i-check-if-a-program-exists-from-a-bash-script/22589429#22589429

另请参阅


1不,如果软件包为“状态:deinstall ok config-files”,dpkg -s 命令也会返回零。你可以自己尝试,在你的系统上运行 dpkg -l 命令并将一个在 dpkg -l 输出的第一列中带有“rc”的软件包传递给 dpkg -s。即使该软件包未安装,dpkg -s 也会成功退出。 - josch
@josch 我已经在这个链接上进一步讨论了可能相关的问题:https://stackoverflow.com/questions/1298066/check-if-an-apt-get-package-is-installed-and-then-install-it-if-its-not-on-linu/54239534#54239534 顺便说一下,我还没有一个完美的解决方案。 - Ciro Santilli OurBigBook.com
在你的观点中,为什么使用 dpkg-query -W --showformat='${Status}\n' ... 的解决方案不完美呢? - josch
@josch 他们是否清楚地记录了确切的字符串输出是一个稳定的API?只是有点谨慎,因为install ok installed这个字符串有点长,不太容易用grep查找。虽然我还没有进行详细的研究。 - Ciro Santilli OurBigBook.com
但是确实,dpkg-query -W --showformat='${db:Status-Status}' 看起来还不错,只输出 installed - Ciro Santilli OurBigBook.com
${Status} 给出了 /var/lib/dpkg/statusStatus 字段的纯值。如果您使用的是 dpkg 1.17.11 或更高版本,那么 ${db:Status-Status} 将给您提供所需的内容,而不包括 "eflag" 或 "want"。关于稳定性:自 1995 年以来,这一点没有改变。对您来说,这是否足够稳定?证明:https://git.hadrons.org/cgit/debian/dpkg/dpkg.git/tree/lib/parsehelp.c?id=1b80fb16c22db72457d7a456ffbf1f70a8dfc0a5#n77 至于文档:只需执行 man dpkg 并查看 INFORMATION ABOUT PACKAGES 部分,所有字段值(如 installed)都在那里有记录。 - josch
@josch,谢谢你提供的链接。是的,这些字符串似乎随处可见,我们可以放心地依赖它们。 - Ciro Santilli OurBigBook.com

更简单的解决方案:
现在有一个“apt list”命令可以列出已安装的软件包。您还可以使用该命令搜索特定的软件包。
apt list <package>

请参阅更多信息,请查看man apt

1并使用 apt show 命令获取更多详细信息。 - wjandrea

你需要检查由dpkg -l打印的状态,例如:
$ dpkg -l firefox-esr vim winff
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                                 Version                 Architecture            Description
+++-====================================-=======================-=======================-=============================================================================
hi  firefox-esr                          52.9.0esr+build2-0ubunt amd64                   Safe and easy web browser from Mozilla
ii  vim                                  2:8.1.1198-0york0~14.04 amd64                   Vi IMproved - enhanced vi editor
rc  winff                                1.5.3-3                 all                     graphical video and audio batch converter using ffmpeg or avconv

这里同时安装了vimfirefox-esr,因此您可以键入:
$ dpkg -l firefox-esr | grep -q ^.i && echo This package is installed. || echo This package is NOT installed.
This package is installed.
$ dpkg -l vim | grep -q ^.i && echo This package is installed. || echo This package is NOT installed.
This package is installed.
$ dpkg -l winff | grep -q ^.i && echo This package is installed. || echo This package is NOT installed.
This package is NOT installed.

在shell脚本中使用特定值作为变量的示例(例如test.sh

#!/bin/sh
PACKAGE="${1}"
INSTALLED=$(dpkg -l | grep ${PACKAGE} >/dev/null && echo "yes" || echo "no")

echo "${PACKAGE} is installed ... ${INSTALLED}"

将其设置为可执行文件并开始执行:
test.sh openssh-server

或者在你的脚本中随意处理


这是不正确的。dpkg -l命令会列出你曾经安装但后来卸载的软件包。而且,像你所做的那样,在dpkg -l输出中使用grep命令也会匹配到软件包描述中的所有文本。正确的方法是使用dpkg-query -W --showformat='${db:Status-Status}' $package命令。 - josch