如何通过命令行安装仅安全更新?

使用sudo apt-get upgrade命令可以安装所有更新,而不仅仅是安全更新。我知道可以使用更新管理器来选择只安装重要的安全更新,但是有没有办法通过命令行来实现这个功能呢?

我猜你是指apt-get(dist-)upgrade吧? - andol
2我不这么认为。dist-upgrade会将整个系统升级到一个新版本。我说的是日常更新,就像在更新管理器中看到的那些更新。 - crenshaw-dev
1哦,我现在明白你在说什么了。哈,我经常运行apt-get update,以至于我不用思考就能打出来。谢谢提醒! - crenshaw-dev
7你应该使用"apt-get dist-upgrade"而不是"apt-get upgrade"。"dist-upgrade"不用于新版本(这是一个与之独立的命令"do-release-upgrade")。使用"dist-upgrade"可以处理新软件包的依赖关系变化,这可能非常重要。 - Kees Cook
1我不认为@KeesCook是正确的。根据apt文档,除非你在不同发行版之间切换,否则你应该使用apt-get upgrade。只有在极少数情况下,才需要进行dist-upgrade操作,并且只有在运行已发布版本(例如,非当前的beta/rc版本)的Ubuntu时才需要升级。 - Jay _silly_evarlast_ Wren
3dist-upgrade是由Update Manager GUI执行的正常操作。对于像内核这样有linux-image-generic包的软件包,根据当前的镜像,例如linux-image-3.x.y-zz-generic(每个版本都是一个单独的包名),dist-upgrade(允许安装新软件包以满足依赖关系)将执行此升级,而upgrade将显示内核包被保留。 - chronitis
4令人惊讶的是,对于这个问题,没有一个好的基于apt-get的答案,考虑到它在每台服务器上都有如此重要的地位。 - Karthik T
其实,ILIV下面的回答非常好。首先,你可以列出需要更新的软件包,然后只运行sudo apt-get install <name>来升级这些软件包。这样可以确保只升级那些存在安全问题的软件包。 - Alexis Wilke
1两个帖子(具有不同的解决方案和不同的解释)。 - jpaugh
10个回答

软件包 unattended-upgrades 提供了自动安装安全更新的功能。

你可以使用这个软件包,但是你也可以手动调用它,而不是配置自动部分:

sudo unattended-upgrade -d --dry-run
sudo unattended-upgrade -d # Idem --debug

如果你想要以安静的方式运行它:
sudo unattended-upgrade

注意:当你调用unattended-upgrade时,末尾不要加上"s"(在较新的版本中有一个符号链接来避免这个问题)。
这假设该软件包已默认安装,很可能是这样。如果没有,默认安装它即可。
sudo apt install unattended-upgrades

另请参阅{{link1:/usr/share/doc/unattended-upgrades/README.md}}。

1要禁用unattended-upgrade的自动执行,您可能需要修改/etc/cron.daily/apt,但不确定这样做是否“正确”。 - Jaime Hablutzel
附注:对于Ubuntu 12.04.5 LTS服务器,默认情况下未安装unattended-upgrades - Raptor
24由于您是通过命令行进行操作,可以使用“-v”来显示信息消息或使用“-d”来显示调试消息。否则,该工具将非常安静,这种情况下您需要检查位于“/var/log/unattended-upgrades”中的日志。您还可以使用“--dry-run”来模拟但不实际升级任何内容。有关更多信息和其他选项,请使用“--help”获取帮助消息。 - ADTC
今天我学到了一些关于unattended-upgrades的东西。谢谢! - Randy L
用于监控进展情况,这只是调试非交互式信息对吗? - Aquarius Power
对我来说,README文件已经被压缩成gzip格式了,所以为了阅读它,我使用了gzip -dc /usr/share/doc/unattended-upgrades/README.md.gz | less命令。 - Gogowitsch
1@Gogowitsch:你会很高兴知道有一个专门的工具可以做到这一点:zless /usr/share/doc/unattended-upgrades/README.md.gz(https://www.thegeekstuff.com/2009/05/zcat-zless-zgrep-zdiff-zcmp-zmore-gzip-file-operations-on-the-compressed-files/) - mwfearnley
我试过这个命令,看起来它正在更新所有东西。例如,它正在更新vim。vim与安全包有什么关系呢? - sgon00
@sgon00 Vim最近发现了一个安全漏洞:https://ubuntu.com/security/notices/USN-6195-1 - undefined

关于如何管理更新的一些建议
这适用于Debian和Ubuntu,但以下是针对Ubuntu的更具体的指示。
只显示安全更新: apt-get -s dist-upgrade |grep "^Inst" |grep -i securi 或 sudo unattended-upgrade --dry-run -d 或 /usr/lib/update-notifier/apt-check -p 显示所有可升级的软件包: apt-get -s dist-upgrade | grep "^Inst"
仅安装安全更新: apt-get -s dist-upgrade | grep "^Inst" | grep -i securi | awk -F " " {'print $2'} | xargs apt-get install

注意事项:

有时Ubuntu会将安全更新显示为来自$release-updates存储库。据我所知,这是因为Ubuntu开发人员也会将安全更新推送到$release-updates存储库,以加快其可用性。
如果是这种情况,您可以执行以下操作仅显示安全更新:
sudo sh -c 'grep ^deb /etc/apt/sources.list | grep security > /etc/apt/sources.security.only.list'

apt-get -s dist-upgrade -o Dir::Etc::SourceList=/etc/apt/sources.security.only.list -o Dir::Etc::SourceParts=/dev/null | grep "^Inst" | awk -F " " {'print $2'}
检查在软件包升级后需要重新启动的服务。事先确定要升级的软件包,并安排重新启动/重启。问题在于,除非重新启动服务,否则它可能仍在使用旧版本的库(最常见的原因),该库在您安装修复安全漏洞的新软件包之前已加载到内存中。
checkrestart -v
但是,请记住,checkrestart可能会列出不一定需要重新启动的进程。例如,PostgreSQL服务可能仍然在内存中保留对已删除的xlog文件的引用,这并不是重新启动服务的有效原因。
因此,使用标准工具来检查这一点的另一种更可靠的方法是使用以下小型bash脚本,我无耻地从https://locallost.net/?p=233中窃取:
它通过保留这些已删除库的副本在活动内存中,检查系统上运行的进程是否仍在使用已删除的库。 ps xh -o pid | while read PROCID; do grep 'so.* (deleted)$' /proc/$PROCID/maps 2> /dev/null if [ $? -eq 0 ]; then CMDLINE=$(sed -e 's/\x00/ /g' < /proc/$PROCID/cmdline) echo -e "\tPID $PROCID $CMDLINE\n" fi done

2我现在才注意到这篇帖子。它非常准确。非常感谢(+1)。 - Danduk82
“checkrestart”是从哪里来的?我在Ubuntu Trusty中找不到它。我找到了“needrestart”,看起来它可以符合您的说明? - Ben XO
它可以在debian-goodies软件包中找到:https://packages.debian.org/wheezy/debian-goodies。还有needrestart。您可以通过运行以下命令在Xenial上找到两者:$apt-cache search checkrestart - ILIV
即使使用sudo,我仍然遇到了“E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)”的错误。这是由于更新中的某个特定问题还是您提供的命令有误? - Nathan Hornby
很有可能是由于 dpkg 的异常终止导致了一个未清除的锁文件。通常情况下不会发生,除非例如某个软件包的安装没有成功完成(磁盘已满等)。你可能无法运行其他 apt-get 和 dpkg 命令,对吗? - ILIV
你对 grep -i securi 的搜索似乎从来都不起作用。包似乎没有被标记为那样的方式。 - vcardillo
$ apt-get -s dist-upgrade |grep "^Inst" |grep -i securi 安装 liboxideqt-qmlplugin [1.17.9-0ubuntu0.16.04.1] (1.18.3-0ubuntu0.16.04.1 Ubuntu:16.04/xenial-updates, Ubuntu:16.04/xenial-security [i386]) [] ...换句话说,它有效。 - ILIV
2如果我在apt-get install命令中不加上-y选项,它会在第一个yes/no问题处停止执行。供您参考。 - Mike Q
我相信 apt-get | grep | xargs apt-get 命令会改变系统认为你必须安装的软件包。虽然 apt-get upgrade 命令会升级所有东西,但它不会将某些随机的依赖项列为通过此方式"必需"的软件包,而运行 apt-get install <pkg> 命令则会这样做。 - Christopher Schultz
非常有帮助!双重grep可以这样替换:grep "^Inst.*securi.*" 也就是说,以Inst开头并包含securi(ty)的内容。 - Steven the Easily Amused
1只安装安全更新会在zsh或bash中出错,除非我添加非交互标志:xargs apt install -y - ctpenrose

替换/etc/apt/preferences为以下内容:
Package: *
Pin: release a=lucid-security
Pin-Priority: 500

Package: *
Pin: release o=Ubuntu
Pin-Priority: 50

现在只需要简单的`apt-get upgrade`命令,就可以升级所有安全更新。
为什么(以及如何)会起作用:首先,首选项文件将Ubuntu发行版中的所有软件包优先级设置为50,这使得它们比已安装的软件包更不受欢迎。源自安全存储库的文件具有默认(500)优先级,因此它们被视为可供安装的软件包。这意味着只有那些被认为比当前已安装软件更为理想的软件才是安全更新。关于固定的更多信息,请参阅apt_preferences manpage
您可以使用`--target-release`选项临时提升某个发行版的更新,该选项适用于`apt-get`和`aptitude`(至少),它允许您固定某些发行版,以便升级它们。
如果您只希望将此方法用于脚本,并不将其作为系统的默认设置,可以将规则放置在其他位置,并使用以下方式替代:
apt-get -o Dir::Etc::Preferences=/path/to/preferences_file upgrade

这将使apt在非默认位置查找首选项文件。
给出的示例首选项文件不适用于第三方存储库,如果您希望对其进行固定,可以使用apt-cache policy轻松确定所需的固定键。

感谢您抽出时间给予详尽的回答。我 觉得 我理解了它的工作原理。但是当我创建了 /etc/apt/preferences 文件并运行 apt-get upgrade 时,它想要升级所有的软件包,而不仅仅是安全更新。在使用 /etc/apt/preferences 文件之前和之后,升级列表完全相同,除了 它不想升级我从源代码构建并用 dpkg 手动安装的 Leafpad 软件包。对我来说这非常奇怪,但可能对您有特殊含义。 - crenshaw-dev
1你可以通过apt-cache policy命令查看正在发生的情况。选择一个没有得到安全修复的软件包,并运行apt-cache policy packagename命令。这将列出各个版本的优先级。你应该能看到不同的行和不同的优先级。如果没有优先级为50的行,那么由于某种原因,固定版本并没有影响到相关的软件包。 - Ressu
2我以前曾经遵循过这个答案。今天我发现,由于这个答案的原因,我的服务器上有68个安全更新包没有被安装,并且没有显示为潜在的安装候选项。这个答案真的不好! - Shade

以下内容在Ubuntu 14.04 LTS中已确认。
使用unattended-upgrade软件包。
查看文件/etc/apt/apt.conf.d/50unattended-upgrades。顶部应该有一个部分:
// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
//  "${distro_id}:${distro_codename}-updates";
//  "${distro_id}:${distro_codename}-proposed";
//  "${distro_id}:${distro_codename}-backports";
};

请注意,它已经被配置为默认情况下仅允许自动升级安全软件包。
类似地修改文件/etc/apt/apt.conf.d/10periodic
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

这将每天自动运行无人值守的安全升级。
现在,要手动运行:sudo unattended-upgrade
要进行干扰测试,而不执行任何操作:sudo unattended-upgrade --dry-run
来源:https://help.ubuntu.com/14.04/serverguide/automatic-updates.html

有没有办法将这个变成每月的日程安排? - mike.b93
1@mike.b93,我相信设置APT::Periodic::Unattended-Upgrade "30";会实现这个目的——每30天一次。 - vcardillo

如果您只希望安装安全更新,可以按照以下步骤进行操作。首先,它会列出所有可升级的软件包,然后筛选出仅来自安全软件源的软件包,将返回的字符串截取到第一个字段,并将其传递给apt-get install以进行软件包更新。
sudo apt list --upgradable | grep security |cut -d\/ -f1|xargs sudo apt-get install -y

2@keypress对于这个优雅的解决方案有了很大的改进:使用--only-upgrade确保不会安装新的软件包。我刚刚用apt进行了测试,它有效果。 - int_ua
如果我理解正确,需要在运行 sudo apt list 前先运行 sudo apt update ,以确保查找到所有新内容。 - mjbeyeler
是的,那是正确的;在能够列出任何安全更新之前,应该已经运行了 apt-get update。 - Micah Butler
请注意,如果任何安装过程需要交互式输入(例如决定是否用新的配置文件覆盖现有的用户编辑的配置文件),那么这个一行命令可能会在中途停止并显示错误(在我的情况下是如此)。最好省略 |xargs sudo ... 部分,只列出软件包,这样您可以逐个安装它们(假设没有太多软件包)。 - joe
对我来说工作很好,谢谢。 - water_ak47

在Debian上,我使用这个命令只进行安全更新:
apt-get install -y --only-upgrade $( apt-get --just-print upgrade | awk 'tolower($4) ~ /.*security.*/ || tolower($5) ~ /.*security.*/ {print $2}' | sort | uniq )

这个(意外地?)不包括各种linux-{modules,images,headers}-*软件包。这对我的目的来说是完美的,因为一个几乎无人值守的系统上有一个繁琐的内核模块,每次内核更新后都需要重新编译。 - Henk Poley
是的,这是个意外 - 我以为linux-*软件包的安全更新也来自安全存储库。但我没有检查过。 - keypress
是的,长期支持硬件增强栈更新来自于*-updates仓库。所以那里没有包含“安全”一词的文本。 - Henk Poley

虽然它看起来相当丑陋,但你可以禁用除了安全仓库之外的所有仓库,然后执行以下操作:
sudo apt-get update && sudo apt-get upgrade

我还没有测试过,但理论上它只会在安全软件库中找到更新并应用它们...

是的,这是可能的。我会调查一下。虽然我不擅长BASH,但我可以尝试编写一个脚本来实现它。 - crenshaw-dev
好的,我禁用了除Ubuntu安全仓库之外的所有仓库,并运行了sudo apt-get update && sudo apt-get upgrade(在执行任何升级之前取消)。然后我重新启用了所有的仓库,运行了sudo apt-get updatee,并打开了更新管理器。标记为安全更新的软件包与apt-get upgrade找到的不完全相同,但它们非常接近--对我来说足够接近了。我仍然希望知道更新管理器是如何做到这一点以及如何从命令行执行相同的操作,但这已经足够了。谢谢! - crenshaw-dev

  • apt-get update: 只是根据现有列表读取存储库中的条目。需要检查有什么新内容。
  • apt-get upgrade: 安装包的所有更新,不包括内核模块。没有发布更新。
  • apt-get dist-upgrade: 安装包的所有更新,包括内核模块。没有发布更新。
  • apt-get 参数 -s: 仅进行测试,不执行任何更改。

这是一个以几种不同方式实现的脚本:
#!/usr/bin/env bash
set -e

# List upgradable packages
apt-get update
apt list --upgradable 2>/dev/null
# List security upgrades
test "$(apt-get upgrade -s -y)" && (apt-get upgrade -s -y)
# List upgradable apt packages then upgrade
apt-get update && apt-get upgrade -y  -V | grep '=>' | awk '{print$1}' && test "$(apt-get upgrade -y)"

我在apt-get和aptitude中都找不到选项,但是有人在SuperUser上提出了同样的问题。唯一的回答是:
Check and adjust /etc/apt/apt.conf.d/50unattended-upgrade. 
Did you replace 'karmic' with the code name of your Ubuntu?

没有关于是否成功的回复。

2似乎该维基页面中描述的方法依赖于将aptitude的--target-release参数设置为<release>-security。就像提问者所说的那样,该方法安装所有升级,而不仅限于安全升级。阅读apt-get和aptitude的手册页,我认为--target-release参数甚至并没有旨在仅限制为安全升级,尽管我不确定它到底是用来做什么的。 - crenshaw-dev