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

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


当前回答

这个就行了。Apt-get install是幂等的。

sudo apt-get install --no-upgrade command

其他回答

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

而不是输出“未安装”。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:没有找到匹配的包。

注意,只有某些包会留下配置文件。像hello这样更简单的包可以直接从安装到未安装,而不需要——purge。

在Ubuntu 20.10上测试。

Python apt包

在Ubuntu 18.04中有一个预安装的名为apt的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脚本中检查程序是否存在?

另请参阅

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

UpAndAdam:

但是,不能简单地依赖这里的返回代码来编写脚本

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

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

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

这对我来说很好……

apt list [packagename]

这似乎是在DPKG和旧apt-*工具之外最简单的方法。

这似乎很有效。

$ sudo dpkg-query -l | grep <some_package_name> | wc -l

如果没有安装,则返回0;如果安装,则返回某个数字> 0。

现在apt-get似乎有一个选项——不升级,只做OP想要的:

——no-upgrade不升级包。当与install一起使用时,no-upgrade将阻止已经安装的包被升级。

Manpage来自https://linux.die.net/man/8/apt-get

因此你可以使用

apt-get install --no-upgrade package

如果不是,包才会被安装。