我正在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
大多数人都会这么做吗?还是有更优雅的解决方案?
当前回答
在本地而不是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文件是一个更新的版本。这样就不会覆盖当前安装的包。
其他回答
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
如果您的包具有命令行接口,则可以在安装之前通过调用它的命令行工具来计算输出,从而检查包是否存在。
这里有一个叫做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;
我根据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,因为正如评论中提到的,这可能会意外地升级你没有计划的包;对于期望稳定的自动化流程来说,这并不是一个好主意。
为了更明确一点,这里有一些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脚本),您可能想要用'gksudo'调用替换'sudo'调用。
对于Ubuntu来说,apt提供了一种相当不错的方式来做到这一点。下面是谷歌Chrome浏览器的一个例子:
grep -qE "(已安装|upgradeable)"|| apt-get install google-chrome-stable
我将错误输出重定向为null,因为apt警告不要使用其“不稳定的cli”。我怀疑列表包是稳定的,所以我认为可以扔掉这个警告。qq让apt变得超级安静。