我正在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
大多数人都会这么做吗?还是有更优雅的解决方案?
我正在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
大多数人都会这么做吗?还是有更优雅的解决方案?
当前回答
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
其他回答
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
我根据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,因为正如评论中提到的,这可能会意外地升级你没有计划的包;对于期望稳定的自动化流程来说,这并不是一个好主意。
这个命令是最容易记住的:
dpkg --get-selections <package-name>
如果已经安装,它会打印:
<包名称>安装
否则它会打印
没有找到匹配<package-name>的包。
这是在Ubuntu 12.04.1 (Precise穿山甲)上测试的。
如果您的包具有命令行接口,则可以在安装之前通过调用它的命令行工具来计算输出,从而检查包是否存在。
这里有一个叫做helm的包的例子。
#!/bin/bash
# Call the command for the package silently
helm > /dev/null
# Get the exit code of the last command
command_exit_code="$(echo $?)"
# Run installation if exit code is not equal to 0
if [ "$command_exit_code" -ne "0" ]; then
# Package does not exist: Do the package installation
else
echo "Skipping 'helm' installation: Package already exists"
fi;
UpAndAdam:
但是,不能简单地依赖这里的返回代码来编写脚本
根据我的经验,你可以依赖dkpg的退出代码。
如果安装了包,dpkg -s的返回码是0,如果没有,返回码是1,所以我找到的最简单的解决方案是:
dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>
这对我来说很好……