如何仅通过命令卸载正在运行的内核?

我想通过命令行安装运行中的内核。
举个例子,我已经安装了一个自定义内核,`linux-image-4.0-1-xyz`,并且已经使用这个内核进行了引导(当我执行`uname -r`时,会得到上述的内核版本)。
现在,我想在不切换到通用内核的情况下删除这个内核。
我尝试了`apt purge -y linux-image-4.0-1-xyz`,这个命令确实运行了,但是我得到了一个“包配置”对话框,其中询问
Do you want to abort removal now?

有两个选择
<Yes> <No>

如何通过命令行选择<No>

您无法安装正在运行的内核版本。 - user880592
1@GabrielaGarcia 是的,你可以。 - fkraiem
@fkraiem 等待您的回答。 :) - user880592
1这似乎是一个关于如何使用键盘选择选项的问题,所以我不太理解那个冗长、分散注意力的段落,讲述了不明智的软件包管理。 - user535733
@Zanna 你能分享一下你使用的确切命令吗?还有,你是从同一个内核卸载的还是从不同的内核卸载的?我正在尝试从同一个内核进行操作。 - Pulkit Lall
就像我之前所说的,运行的内核,但是我想我实际上只是删除了它,然后重新安装,而不是使用APT卸载。我刚刚尝试了一下(sudo apt remove linux-image-$(uname -r)),我确实看到了你提到的对话框。我用箭头键移动到“NO”,然后按回车键,内核包被卸载了。要再次安装它,我必须运行sudo apt install linux-image-$(uname -r) linux-modules-extra-$(uname -r),因为模块包已被移除,但没有自动引入。不过,我想知道为什么你想要卸载正在运行的内核呢? - Zanna
@Zanna 我正在尝试使用命令行。虽然是通过命令行,但如何选择“NO”呢? - Pulkit Lall
你是指你想避免对话吗? - Zanna
@Zanna 是的,请 - Pulkit Lall
4个回答

实际上,这个问题的答案比我预期的要烦人一些。是的,是的,这是危险的,而且维护者们为了这个原因特意让它变得非常困难。但是如果你试图自动化你的基础架构来做类似的事情,我相信你确实知道你在做什么。在我的情况下,是因为我正在尝试在另一个脚本中清除(当前运行的)内核,并验证是否确实有一个更新的内核可用。
通过检查debconf-get-selections的输出,确实有一个选项可以为您预先选择这个值:
$ sudo debconf-get-selections | grep -B1 linux-base
# Abort kernel removal?
linux-base      linux-base/removing-running-kernel      boolean true

如果你使用debconf-set-selections将其设置为"false",然而,什么都不会改变。你仍然会被提示。
这导致了一些深入研究是什么在调用这个。事实证明,在相对较新的(Buster)Debian版本和可能的Ubuntu版本上,有一个单独的Perl脚本叫做linux-check-removal,它的目的似乎是忽略、重置,然后提示这个警告。

http://manpages.ubuntu.com/manpages/cosmic/man1/linux-check-removal.1.html

这个脚本是从给定内核的prerm dpkg脚本中调用的,例如。
$ cat /var/lib/dpkg/info/linux-image-4.19.0-13-amd64.prerm
#!/bin/sh -e

version=4.19.0-13-amd64
image_path=/boot/vmlinuz-$version

if [ "$1" != remove ]; then
    exit 0
fi

linux-check-removal $version

if [ -d /etc/kernel/prerm.d ]; then
    DEB_MAINT_PARAMS="$*" run-parts --report --exit-on-error --arg=$version \
              --arg=$image_path /etc/kernel/prerm.d
fi

exit 0

验证其运行情况:
$ sudo linux-check-removal $(uname -r)
# "Yes" selected
E: Aborting removal of the running kernel
$ echo $?
1
$ sudo linux-check-removal $(uname -r)
# "No" selected
W: Removing the running kernel
$ echo $?
0

所以,实际的解决方案是用返回0的方式覆盖这个命令。
然而,尽管该命令使用了相对调用,但在从dpkg调用时,此脚本中的$PATH不包括/usr/local目录。因此,像你期望的那样,在/usr/local/bin中简单地覆盖脚本是行不通的。为了证明这一点,我修改了prerm脚本以打印$PATH和which值,然后调用apt remove命令。
Removing linux-image-4.19.0-13-amd64 (4.19.160-2) ...
PATH: /usr/sbin:/usr/bin:/sbin:/bin
WHICH: /usr/bin/linux-check-removal
W: Removing the running kernel

因此,我们被迫暂时将真正的 /usr/bin/linux-check-removal 移出路径,并安装一个简单的占位脚本,它只返回0。我能想到的最简单的方法是:
$ sudo mv /usr/bin/linux-check-removal /usr/bin/linux-check-removal.orig
$ echo -e '#!/bin/sh\necho "Overriding default linux-check-removal script!"\nexit 0' | sudo tee /usr/bin/linux-check-removal
$ sudo chmod +x /usr/bin/linux-check-removal

你可以选择删除echo行,但我将其包含在演示中。
现在,您可以成功地无需交互地清除正在运行的内核版本。
$ sudo apt purge -y linux-image-4.19.0-13-amd64
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  linux-headers-4.19.0-12-amd64 linux-headers-4.19.0-12-common linux-image-4.19.0-12-amd64
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
  linux-image-4.19.0-13-amd64*
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 270 MB disk space will be freed.
(Reading database ... 104395 files and directories currently installed.)
Removing linux-image-4.19.0-13-amd64 (4.19.160-2) ...
Overriding default linux-check-removal script!
I: /vmlinuz.old is now a symlink to boot/vmlinuz-4.19.0-12-amd64
I: /initrd.img.old is now a symlink to boot/initrd.img-4.19.0-12-amd64
/etc/kernel/postrm.d/initramfs-tools:
update-initramfs: Deleting /boot/initrd.img-4.19.0-13-amd64
/etc/kernel/postrm.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.19.0-14-amd64
Found initrd image: /boot/initrd.img-4.19.0-14-amd64
Found linux image: /boot/vmlinuz-4.19.0-12-amd64
Found initrd image: /boot/initrd.img-4.19.0-12-amd64
done
(Reading database ... 99993 files and directories currently installed.)
Purging configuration files for linux-image-4.19.0-13-amd64 (4.19.160-2) ...

然后恢复原始的linux-check-removal:
$ sudo mv /usr/bin/linux-check-removal.orig /usr/bin/linux-check-removal

注意:尽管可能可以这样做,但强烈不建议删除正在运行的内核。如果该内核是唯一安装的,并且在卸载或删除后重新启动或断电,您的系统将无法启动,可能需要重新安装。恢复模式将无法正常工作。如果发生这种情况,您可以从一个活动系统引导以恢复数据,并可能将内核文件复制到正确的位置。通常没有理由卸载正在运行的内核。所以您可能不应该按照这个答案的方法 :)
你在评论中提到你想避免使用软件包管理工具删除正在运行的内核时出现的对话框。我能想到的唯一方法是删除所有属于正在运行的内核的文件。
我们将使用“locate”命令来查找内核文件。首先更新它的数据库。
sudo updatedb

如果由于某种原因您不想删除内核的dpkg数据库记录,可以运行以下命令代替上述操作:
sudo updatedb --prunepaths=/var/lib/dpkg

找到正在运行的内核及其模块等:
locate -be $(uname -r)

检查列表。要删除属于内核的所有文件,甚至是在/usr/src中的许多小文件,从locate中省略-b标志。当你有想要的列表时,将结果传递到xargs以删除这些文件。
locate -be $(uname -r) -0 | xargs -0 sudo rm -r

为了使xargs交互式,您可以添加-p标志,它会提示您确认是否确实要在列表中的每个文件上运行rm命令。如果您从locate中省略-b并将-p添加到xargs中,您将需要长时间进行确认。
实际上并不需要使用-0标志来获取空字符分隔的输出,因为除非您或其他人对其进行了调整,否则所有这些文件都将具有合理的名称。但是,使用null分隔符与xargs一起使用是一个好习惯,因为如果任何文件名包含空格,可能会出现错误。
为了防止GRUB尝试引导或给您提供引导不存在内核的选项,请更新配置。
sudo update-grub

我刚刚测试了一下,现在正在使用这个系统输入这段文字。我是通过删除运行中的内核来实现的(我从一个旧的Ubuntu内核启动来进行操作,因为我通常使用一个dpkg不知道的补丁内核,并且我不想再次删除它并重新编译)。
内核位于RAM中。它在启动时加载到RAM中,所以现在不需要它的文件。然而,我现在将使用以下命令重新安装内核:
sudo apt update
sudo apt install --reinstall linux-image-$(uname -r) \
                             linux-modules-extra-$(uname -r) \
                             linux-headers-$(uname -r) \
                             linux-modules-$(uname -r)

由于您似乎对非交互式过程感兴趣,我想提醒您,如果您希望将任何输出内容保存到文件以供以后使用,可以使用apt-get而不是apt

1工作得很顺利,谢谢!关于“极不可取”的问题——这取决于具体情况。如果您是在一个全新的云虚拟机上进行自动化构建(例如创建带有自定义内核的AWS AMI),那么这种方法一点问题都没有。 - nirvana-msu

使用 TAB 键来移动选择器。使用 ENTER 键来选择。

需要通过命令行进行更改。 - Pulkit Lall

阻塞程序

这是/usr/bin/linux-check-removal在提示。

$ /usr/bin/linux-check-removal

Usage: /usr/bin/linux-check-removal VERSION

This command is intended to be called from the prerm maintainer scripts
of Linux kernel packages.

The VERSION argument must be the kernel version string as shown by
'uname -r' and used in filenames.

If the currently running kernel matches VERSION, linux-check-removal
will normally prompt the user to confirm this potentially dangerous
action and will fail if the user chooses to abort.  However, if the
current environment is in a chroot or container, or if debconf
prompts are disabled, it will always succeed without prompting.

解决方案

不必复杂化。只需阅读其内容:

如果禁用了debconf提示,它将始终在不提示的情况下成功执行。

禁用Debian提示之前添加DEBIAN_FRONTEND=noninteractive

DEBIAN_FRONTEND=noninteractive apt purge --autoremove --assume-yes linux-image-$(uname --kernel-release)

无需提示即可正常工作。