我正在使用 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
这是大多数人会做的吗?还是有更优雅的解决方案?
要检查是否packagename
已安装,请键入:
dpkg -s <packagename>
您还可以使用dpkg-query
具有更简洁的输出来满足您的目的,并且也可以接受通配符。
dpkg-query -l <packagename>
要查找哪个包拥有command
,请尝试:
dpkg -S `which <command>`
有关详细信息,请参阅文章Find if package is installed in Linux and dpkg cheat sheet。
更明确地说,这里有一个 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脚本),您可能希望将 'sudo' 调用替换为 'gksudo' 调用。
这个单线返回 1(已安装)或 0(未安装)“nano”包...
$(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed")
即使包不存在或不可用。
如果未安装,下面的示例将安装“nano”包...
if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
apt-get install nano;
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
而不是输出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
leave certbot
in state 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 上测试。
Pythonapt
包
在 Ubuntu 18.04 中有一个预安装的 Python 3 包apt
,它公开了一个 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
文件
也可以看看
Ubuntu 添加了它的“Personal Package Archive”(PPA),而 PPA 包有不同的结果。
未安装本机Debian存储库包:
~$ dpkg-query -l apache-perl
~$ echo $?
1
在主机上注册并安装的 PPA 包:
~$ dpkg-query -l libreoffice
~$ echo $?
0
在主机上注册但未安装的 PPA 包:
~$ dpkg-query -l domy-ce
~$ echo $?
0
~$ sudo apt-get remove domy-ce
[sudo] password for user:
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package domy-ce is not installed, so not removed
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
还发布在:测试是否在 APT 中安装了一个包
但是,您不能简单地依靠此处的返回码来编写脚本
根据我的经验,您可以依赖 dkpg 的退出代码。
dpkg -s的返回码是0如果安装了包,如果没有安装,则返回1,所以我找到的最简单的解决方案是:
dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>
这对我来说可以...
这似乎工作得很好。
$ sudo dpkg-query -l | grep <some_package_name> | wc -l
0
某个数字> 0
。我已经根据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
比大多数其他错误消息更容易解析,因为它不包括“卸载”之类的状态。它还可以同时检查多个包,而仅使用错误代码是无法做到的。
说明/示例:
$ 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
,因为正如评论中所提到的,这可能会意外地升级您不打算使用的软件包;对于预期稳定的自动化流程来说,这并不是一个好主意。
现在似乎apt-get
有一个选项--no-upgrade
可以满足 OP 的要求:
--no-upgrade
不要升级软件包。与 install 一起使用时,no-upgrade 将阻止列出的软件包在已安装的情况下升级。
来自https://linux.die.net/man/8/apt-get的手册页
因此你可以使用
apt-get install --no-upgrade package
并且package
只有在没有安装时才会安装。
这会做到的。apt-get install
是幂等的。
sudo apt-get install --no-upgrade command
我发现以前的答案中的所有解决方案都可能会产生误报,如果安装包然后删除,但安装包仍保留在系统上。
要复制:
安装包apt-get install curl
删除包apt-get remove curl
现在测试以前的答案。
以下命令似乎可以解决这种情况:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'
这将导致最终安装或未安装。
$name="rsync"
[ `which $name` ] $$ echo "$name : installed" || sudo apt-get install -y $name
采用:
apt-cache policy <package_name>
如果没有安装,会显示:
Installed: none
否则会显示:
Installed: version
受克里斯回答的启发:
#! /bin/bash
installed() {
return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}
pkgs=(libgl1-mesa-dev xorg-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools)
missing_pkgs=""
for pkg in ${pkgs[@]}; do
if ! $(installed $pkg) ; then
missing_pkgs+=" $pkg"
fi
done
if [ ! -z "$missing_pkgs" ]; then
cmd="sudo apt install -y $missing_pkgs"
echo $cmd
fi
此功能已存在于 Ubuntu 和 Debian 的command-not-found
软件包中。
这个命令是最难忘的:
dpkg --get-selections <package-name>
如果已安装,则会打印:
<包名> 安装
否则会打印
找不到与 <package-name> 匹配的包。
这是在 Ubuntu 12.04.1 (Precise Pangolin) 上测试的。
which <command>
if [ $? == 1 ]; then
<pkg-manager> -y install <command>
fi
apt list [packagename]
似乎是 dpkg 和较旧的apt -* 工具之外最简单的方法。
对于 Ubuntu,apt
提供了一种相当不错的方法来做到这一点。以下是谷歌浏览器的示例:
apt -qq list google-chrome-stable 2>/dev/null | grep -qE "(installed|upgradeable)" || apt-get install google-chrome-stable
我将错误输出重定向到 null,因为apt
警告不要使用其“不稳定的 cli”。我怀疑 list 包是稳定的,所以我认为可以丢弃这个警告。-qq 使 apt 超级安静。
在本地而不是在 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 文件是较新的版本。那么这不会覆盖当前安装的包。
如果仅使用 awk 安装 else 1,则会显式打印 0:
dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 0;exit}{print 1}'
或者,如果您更喜欢 1 表示已安装,否则为 0:
dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 1;exit}{print 0}'
** 用你的包名替换 PKG
便利功能:
installed() {
return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}
# usage:
installed gcc && echo Yes || echo No
#or
if installed gcc; then
echo yes
else
echo no
fi
有点基于你的,只是更“优雅”了一点。只是因为我很无聊。
#!/bin/bash
FOUND=("\033[38;5;10m")
NOTFOUND=("\033[38;5;9m")
PKG="${@:1}"
command ${PKG} &>/dev/null
if [[ $? != 0 ]]; then
echo -e "${NOTFOUND}[!] ${PKG} not found [!]"
echo -e "${NOTFOUND}[!] Would you like to install ${PKG} now ? [!]"
read -p "[Y/N] >$ " ANSWER
if [[ ${ANSWER} == [yY] || ${ANSWER} == [yY][eE][sS] ]]; then
if grep -q "bian" /etc/os-release; then
sudo apt-get install ${PKG}
elif grep -q "arch" /etc/os-release; then
if [[ -f /bin/yay ]] || [[ -f /bin/yaourt ]]; then
yaourt -S ${PKG} 2>./err || yay -S ${PKG} 2>./err
else
sudo pacman -S ${PKG}
fi
elif grep -q "fedora" /etc/os-release; then
sudo dnf install ${PKG}
else
echo -e "${NOTFOUND}[!] This script couldn't detect your package manager [!]"
echo -e "${NOTFOUND}[!] Manually install it [!]"
fi
elif [[ ${ANSWER} == [nN] || ${ANSWER} == [nN][oO] ]]; then
echo -e "${NOTFOUND}[!] Exiting [!]"
fi
else
echo -e "${FOUND}[+] ${PKG} found [+]"
fi
建议使用以下内容的答案:
dpkg-query --showformat '${db:Status-Status}\n' --show $package | grep -q '^installed$'
dpkg-query --showformat '${Status}\n' --show $package | grep -q '^install ok installed$'
是正确的。
但是,如果您安装了软件包dpkg-dev
,并且您不仅想检查是否安装了软件包,而且还:
那么您可以滥用该dpkg-checkbuilddeps
工具来完成这项工作:
dpkg-checkbuilddeps -d apt /dev/null
这将检查是否安装了apt。
下面将检查 apt 是否安装在至少 2.3.15 版本中并且 grep 是否安装为 amd64 以及虚拟包x-window-manager
是否由一些已安装的包提供:
dpkg-checkbuilddeps -d 'apt (>= 2.3.15), grep:amd64, x-window-manager' /dev/null
dpkg-checkbuilddeps 的退出状态会告诉脚本是否满足依赖关系。dpkg-checkbuilddeps
由于此方法支持传递多个包,因此即使要检查是否安装了多个包,也只需运行一次。
我使用以下方式:
which mySQL 2>&1|tee 1> /dev/null
if [[ "$?" == 0 ]]; then
echo -e "\e[42m MySQL already installed. Moving on...\e[0m"
else
sudo apt-get install -y mysql-server
if [[ "$?" == 0 ]]; then
echo -e "\e[42mMy SQL installed\e[0m"
else
echo -e "\e[42Installation failed\e[0m"
fi
fi
我使用这个解决方案,因为我发现它最简单。
function must_install(){
return "$(apt -qq list $var --installed 2> /dev/null |wc -l)"
}
function install_if() {
unset install
for var in "$@"
do
if $(must_install $var)
then
install+="${var} "
fi
done
if [ -n "$install" ];
then
sudo apt-get install -qy $install
fi
}
巧妙的是,must_install
返回 1 或 0,然后从调用中将其解释为 true 或 false if
,因此我们不需要任何test
using []
。
install_if
接受由空格分隔的任意数量的包。
该问题apt
不打算在脚本中使用,因此这可能随时停止工作。8)