我正在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
大多数人都会这么做吗?还是有更优雅的解决方案?
当前回答
我发现在之前的答案中,如果安装了一个包,然后删除了它,但是安装包仍然在系统上,那么所有的解决方案都可能产生假阳性。
复制:
安装包apt-get Install curl 删除包apt-get删除卷曲
现在测试一下前面的答案。
下面的命令似乎可以解决这个问题:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'
这将导致最终安装或不安装。
其他回答
如果您的包具有命令行接口,则可以在安装之前通过调用它的命令行工具来计算输出,从而检查包是否存在。
这里有一个叫做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,因为正如评论中提到的,这可能会意外地升级你没有计划的包;对于期望稳定的自动化流程来说,这并不是一个好主意。
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
现在apt-get似乎有一个选项——不升级,只做OP想要的:
——no-upgrade不升级包。当与install一起使用时,no-upgrade将阻止已经安装的包被升级。
Manpage来自https://linux.die.net/man/8/apt-get
因此你可以使用
apt-get install --no-upgrade package
如果不是,包才会被安装。
既然你提到了Ubuntu,而且你想以编程的方式做到这一点(虽然也可以使用dpkg的变体,但实现起来会更复杂),这(这)肯定是可行的:
#!/bin/bash
pkgname=mutt
which $pkgname > /dev/null;isPackage=$?
if [ $isPackage != 0 ];then
echo "$pkgname not installed"
sleep 1
read -r -p "${1:-$pkgname will be installed. Are you sure? [y/N]} " response
case "$response" in
[yY][eE][sS]|[yY])
sudo apt-get install $pkgname
;;
*)
false
;;
esac
else
echo "$pkgname is installed"
sleep 1
fi
尽管为了POSIX兼容性,您可能希望使用命令-v,正如在另一个类似问题中提到的那样。
这样的话, 在上面的代码示例中,$pkgname > /dev/null应该被命令-v $pkgname替换。