我正在使用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>`
更多细节请参见文章在Linux中查找软件包是否已安装和dpkg速查表。
dpkg -s
在缺少软件包时返回1,在有软件包时返回0,这是应该的。在早期(或最近)版本中有什么不同吗? - MestreLiondpkg -s
命令会在一个软件包被安装并移除后返回零,此时会显示 Status: deinstall ok config-files
或类似的结果,因此这是“正常”的情况,对我来说,这不是一个安全的测试方式。在这种情况下,dpkg-query -l
命令似乎也无法返回有用的结果。 - keen更明确地说,以下是一个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 -W -f='${Status}' MYPACKAGE | grep -q -P '^install ok installed$'; echo $?
- ThorSummonergrep -P
。 - tripleeedpkg-query -W -f='${Status}' nano
的输出中不包含 "ok installed",那么执行 apt install nano
。不需要使用 grep -c
,只需使用 grep
的退出状态。 - Stephen Ostermillerdpkg-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
certbot
以“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上测试通过。
Python apt
软件包
Ubuntu 18.04中有一个预安装的Python 3软件包,名为apt
,它提供了Python apt接口!
可以查看一个检查软件包是否安装并在未安装时安装它的脚本:How to install a package using the 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
中
另请参阅
sudo apt install hello; dpkg -s hello; echo $?; sudo apt remove hello; dpkg -s hello; echo $?
。您能否提供更多细节? - Ciro Santilli OurBigBook.comdpkg remove package-name
不会导致dpkg -s package-name
返回0
的信息,这几乎让我发疯了。实际上,它的状态是已卸载而不仅仅是未安装!哎呀,Debian 真是...特别。 - TNTA native Debian repository package is not installed:
~$ dpkg-query -l apache-perl
~$ echo $?
1
A PPA package registered on the host and installed:
~$ dpkg-query -l libreoffice
~$ echo $?
0
A PPA package registered on the host, but not installed:
~$ 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软件包
但是在脚本中,您不能仅仅依赖于返回代码。
根据我的经验,您可以依赖于dpkg的退出代码。
dpkg -s的返回代码为0表示软件包已安装,为1表示未安装,因此我发现最简单的解决方案是:
dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>
对我来说它工作得很好...
apt-get remove <package>
之后,即使软件包已被“卸载”,运行dpkg -s <package>
仍然会返回0。 - ThorSummoner我选择了基于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
,因为如评论中所述,这可能会意外升级您没有计划的软件包。对于预期稳定的自动化过程来说,这不是一个好主意。
[[ ! -z $MISSING ]] && sudo apt-get install $MISSING
这么简单。 - Shenkgrep
参数为 grep -v ' install$'
(注意前导空格)。 - RobMapt-get --no-upgrade install
是幂等的。sudo apt-get install --no-upgrade command
apt-get install
命令虽然本身是幂等的,但是这样做可能是不合适的。在我的场景中,我正在使用Ansible的原始模块在远程系统上安装一个包,如果我不加判断地运行apt-get install
,它每次都会报告系统已修改。通过添加条件语句可以解决这个问题。 - JBentleyapt-get install --no-upgrade package-name
而不是普通的install
,因为如果有可用的升级,install
会升级包。 - Mikko Rantalainen看起来这个相当有效。
$ sudo dpkg-query -l | grep <some_package_name> | wc -l
0
,如果已安装,则返回> 0
的某个数字。grep | wc -l
是一个反模式。如果要检查某个东西是否存在,只需使用 grep -q
即可。如果要计算出现次数(这在大多数情况下并不常用),请使用 grep -c
。 - tripleeedpkg -s zip | grep -c "Package: zip"
?(以zip作为示例包) - David Tabernero M.grep -q 'Package: zip'
返回一个退出代码,该代码指示是否找到结果而无需打印任何内容。 - tripleee受Chris回答的启发:
#! /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
command -v <command>
而不是which <command>
。另请参见如何在Bash脚本中检查程序是否存在。 - jwwapt install
已经安装的软件包也会将其标记为手动安装,如果该软件包以前被设置为自动安装,则会产生非常不希望的副作用。 - MestreLion