如何在Ubuntu 20.04 LTS中轻松删除旧内核?

我有一些不再使用的旧Linux内核版本,所以我试图将它们删除。
已安装内核列表:dpkg --list | grep linux-image
linux-image-5.4.0-26-generic (5.4.0-26.30)   
linux-image-5.4.0-33-generic (5.4.0-33.37)
linux-image-5.4.0-37-generic (5.4.0-37.41)

2sudo apt autoremove可以帮助吗?你在询问哪些内核呢? - Pilot6
1你试过 sudo apt autoremove 吗?请编辑你的问题并添加所有新的信息。还请说明是否手动安装了内核或其他特殊操作。 - user68186
1这些是常规的Ubuntu内核。autoremove应该可以将它们删除。 - Pilot6
1嗯。显示完整输出。dpkg --list 的输出包括已经被删除的内核。第一列将指示哪些软件包被删除(rc)和哪些被安装(ii)。 - user535733
1有几个优秀的内核删除管理器类型的脚本可供选择。我曾经长时间使用这个的服务器版本,但现在使用这个。请注意,由于autoremove在使用主线内核时会出现问题,所以我无法使用它。手动方法在清理100个内核时变得繁琐。 - Doug Smythies
对我来说,系统更新程序会在安装新的内核版本后的某个时候自动删除它们。 - raj
如果你在这里搜索,可能是 synaptic 的一个 bug。当我尝试使用 synaptic 删除旧的内核版本时,出现了一个错误,但是使用 apt purge 命令可以成功删除。 - quant2016
11个回答

这是删除未使用内核的步骤。
检查当前正在运行的内核:
uname -a
Linux blackhole 5.6.13-050613-lowlatency #202005141310 SMP PREEMPT Thu May 14 13:17:41 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

我正在运行的是5.6.13-050613-lowlatency 列出您操作系统中安装的所有内核:
dpkg --list | egrep -i --color 'linux-image|linux-headers|linux-modules' | awk '{ print $2 }'
linux-headers-5.6.11-050611
linux-headers-5.6.11-050611-lowlatency
linux-headers-5.6.13-050613
linux-headers-5.6.13-050613-lowlatency
linux-image-unsigned-5.6.11-050611-lowlatency
linux-image-unsigned-5.6.13-050613-lowlatency
linux-modules-5.6.11-050611-lowlatency
linux-modules-5.6.13-050613-lowlatency

卸载您不需要的内核。
sudo apt purge linux-headers-5.6.11-050611  linux-headers-5.6.11-050611-lowlatency linux-image-unsigned-5.6.11-050611-lowlatency linux-modules-5.6.11-050611-lowlatency

真的吗?ls -1 /boot/vm* 不会列出你的内核,包括当前的吗? - Paul Benson
@PaulBenson 您的命令没有列出软件包。因此,您无法在aptdpkg中使用这些名称来卸载内核。 - Michal Przybylowicz
7有没有自动化的方法来做这个?我在列表中安装了超过50个内核,其中很多带有“un”标签。 - staticdev
虽然这看起来很繁琐,但似乎是唯一可行的方法。我本来希望能自动完成,但是我找不到一个好用的脚本。我的临时解决办法是使用 sudo apt-get purge linux-*-*-5.*(如果需要,可以删除一个 -* 来捕捉其他模块,并对 4.* 采取同样的操作),然后手动检查输出结果。这将获取所需的软件包,但可能过于激进 - 请仔细检查输出结果,以避免系统崩溃。 - Betaminos
很抱歉出现了重复评论,StackExchange在5分钟后就不允许我编辑评论了。我找到了一个不错的脚本:https://www.tecmint.com/remove-old-kernel-in-debian-and-ubuntu/(即`purge-old-kernels`,它是`byobu`软件包中包含的脚本)。这可能会有所帮助,但我已经清理了所有旧内核,暂时无法进行测试。 - Betaminos
假设我构建并安装了一个更新的内核。然后,在较旧的内核下启动并使用此命令。尽管在GRUB中列出了新的内核,但它未显示在列表中。这是否意味着它没有正确安装?(我正在解决新内核无法正常引导的问题)。 - MadPhysicist
谢谢。对我来说有效。 - karthik nair
速记(自行承担风险!):sudo apt remove --purge $(dpkg --list | egrep -i --color 'linux-image|linux-headers|linux-modules' |grep -v $(uname -r)| awk '{ print $2 }') - gkephorus

你可以试一下这个脚本

remove_old_kernels.sh

#!/bin/bash
# Run this script without any param for a dry run
# Run the script with root and with exec param for removing old kernels after checking
# the list printed in the dry run

uname -a
IN_USE=$(uname -a | awk '{ print $3 }')
echo "Your in use kernel is $IN_USE"

OLD_KERNELS=$(
    dpkg --list |
        grep -v "$IN_USE" |
        grep -Ei 'linux-image|linux-headers|linux-modules' |
        awk '{ print $2 }'
)
echo "Old Kernels to be removed:"
echo "$OLD_KERNELS"

if [ "$1" == "exec" ]; then
    for PACKAGE in $OLD_KERNELS; do
        yes | apt purge "$PACKAGE"
    done
else
    echo "If all looks good, run it again like this: sudo remove_old_kernels.sh exec"
fi

像这样运行它进行试运行:
remove_old_kernels.sh

如果一切看起来正常,请再次运行它,就像这样:
sudo remove_old_kernels.sh exec

太棒了,它在PopOS 20.04上完美运行。 - hernanvicente
标题和模块除了通用部分还被删除了。如何处理这种情况? - Jinna Balu
你可以更改脚本,只搜索linux-image,如果你真的想保留其他内容。 - Alex Burdusel
每当我运行这个脚本并删除一个条目时,我都会卡住,无法在菜单上选择修改grub文件的选项。有没有办法解决这个问题?当我手动删除内核时,我可以在菜单中进行选择以继续操作。 - MadPhysicist
1在Ubuntu 20.04上运行得非常顺畅。 - Andy
这个脚本有一个问题,它试图移除以下软件包:
  • linux-headers-generic
  • linux-image-generic
而我的内核版本是5.17.5-76051705-generic,它试图移除linux-headers-5.17.5-76051705。我想它也需要过滤掉这些软件包。除此之外,这个脚本非常完美!它移除了我系统中很多旧的软件包。但可惜的是dpkg不愿意移除模块的目录...
- eitch
为什么要使用循环?这样会更合适吗?apt purge "$PACKAGE" $OLD_KERNELS然后apt会一次性将它们移除,并显示释放了多少空间。 - rubo77
我在https://github.com/rubo77/remove-old-kernels创建了这个脚本的增强版本。 - rubo77

只是稍微延伸一下Michal的答案。我不想每次都要键入要删除的内核,所以决定使用文件代替。

将您当前拥有的所有内核写入文件中。

dpkg --list | egrep -i --color 'linux-image|linux-headers|linux-modules' | awk '{ print $2 }' > kernels.txt

使用grep命令从文件中过滤掉当前正在使用的内核。
grep -v $(uname -r) kernels.txt > kernels_to_delete.txt

验证您当前的内核不在删除列表中。不要跳过这一步。确保您不会错误地删除所有内核。
grep $(uname -r) kernels_to_delete.txt

一次性删除所有未使用的内核。
cat kernels_to_delete.txt | xargs sudo apt purge -y

只需使用这个:
sudo apt-get autoremove --purge

7请注意,此命令不仅会删除旧内核,还会删除任何不再作为其他软件包的依赖项以及其配置文件所需的软件包。 - BeastOfCaerbannog
@BeastOfCaerbannog,好的,谢谢你的澄清。 - Ihor Romanyshyn
16无法在20.04版本上运行(从18.04升级,再从16.04升级)。我可以列出这些软件包,但Ubuntu不会自动删除旧内核。 - gavioto20
1@gavioto20 奇怪,我也使用了Ubuntu 20.04,这个命令确实可以删除旧内核。它只会删除未被活跃使用的内核。 - Melroy van den Berg
说实话,我在安装新内核失败后尝试运行这个命令,因为/boot空间不足。不幸的是,该命令再次尝试安装新内核,结果又失败了 - 可能是因为它还没有完成删除所有旧镜像的操作。:/ 最终,我选择了@eitch的脚本,这个方法非常成功。 - undefined

@alex Burdusel的脚本更新如下:
#!/bin/bash -e
# Run this script without any arguments for a dry run
# Run the script with root and with exec arguments for removing old kernels and modules after checking
# the list printed in the dry run

uname -a
IN_USE=$(uname -a | awk '{ print $3 }')
echo "Your in use kernel is $IN_USE"

OLD_KERNELS=$(
    dpkg --get-selections |
        grep -v "linux-headers-generic" |
        grep -v "linux-image-generic" |
        grep -v "linux-image-generic" |
        grep -v "${IN_USE%%-generic}" |
        grep -Ei 'linux-image|linux-headers|linux-modules' |
        awk '{ print $1 }'
)
echo "Old Kernels to be removed:"
echo "$OLD_KERNELS"

OLD_MODULES=$(
    ls /lib/modules |
    grep -v "${IN_USE%%-generic}" |
    grep -v "${IN_USE}"
)
echo "Old Modules to be removed:"
echo "$OLD_MODULES"

if [ "$1" == "exec" ]; then
  apt-get purge $OLD_KERNELS
  for module in $OLD_MODULES ; do
    rm -rf /lib/modules/$module/
  done
fi

这解决了它试图删除以下软件包的问题:
linux-headers-generic
linux-image-generic
linux-headers-5.17.5-76051705 # if 5.17.5-76051705-generic is the current kernel

这个脚本被修改了,可以一次性清除所有的软件包,并删除/lib/modules/中剩余的模块目录。

我会添加其他内容;echo "使用$0 exec再次调用此脚本以真正卸载这些内核" - rubo77
请参考以下链接了解如何移除旧内核:https://github.com/rubo77/remove-old-kernels - rubo77

为了轻松删除旧版本内核,例如从4.0版本开始的内核等。
sudo apt-get purge linux-image-4.*

你确定 linux-image-4.* 会删除 "旧的" linux-image-5.4.xxx 内核镜像吗? - Simon Sudler
3当然不是,你怎么能指望这样删除起始5个内核呢?你可以相应地更新命令。我只是举了一个例子。对于linux-image-5.4.xxx,请使用sudo apt-get purge linux-image-5.4.*等等。 - shivam singh
你还需要为头文件执行以下命令:sudo apt purge linux-headers-4.* - Damien

autoremove只会删除自动安装的软件包。如果您曾手动更新或添加过内核软件包,autoremove将不会删除它。如果您曾“保留”某个内核版本,autoremove也不会删除它。如果您想知道为什么Ubuntu会用不再使用的内核填满启动分区,很可能是这两个原因之一。

# Unhold all packages
dpkg --get-selections | grep hold | awk '{ print $1, "install" }' | dpkg --set-selections

# Mark all "manually installed" kernel packages as "automatically installed"
for f in $(apt-mark showmanual | grep linux-); do
    apt-mark auto $f
done

# Remove all packages that are no longer needed
apt-get -y autoremove --purge

我从之前的答案中得出了这个脚本。 它应该保留当前内核和上一个内核。 如之前的答案所述,先执行一次不带任何参数的试运行。 然后以root身份运行,并使用一个参数'exec'来实际删除旧的内核。
#!/bin/bash -e

IN_USE=$(uname -a | awk '{ print $3 }')
echo "Your in use kernel is $IN_USE"

CUR_KERNELS=$(dpkg --get-selections | grep linux-image | grep install | awk '{print $1}')
echo
echo "Current Kernels are:"
echo "$CUR_KERNELS"

OLD_KERNELS=$(dpkg --get-selections | grep linux | grep deinstall | awk '{print $1}')
echo
echo "Old Kernels to be removed:"
echo "$OLD_KERNELS"

if [ "$1" == "exec" ]; then
  apt-get purge $OLD_KERNELS
fi

关于简单的解决方案,可以使用一个名为linux-purge的实用工具,该工具专门用于此目的,并且应该适用于12.04之后的任何Ubuntu版本。该实用工具是使用Bash实现的。 这里是我对另一个类似问题的更详细回答。

脚本的进一步改进:

感谢 @eitch

此版本过滤掉了较新的内核。在早期版本中,较新的内核也会被清除。

此脚本默认为“模拟运行”,但如果 $1 不是“模拟运行”或“执行”,则显示使用帮助文本。

#!/bin/bash -e

# Function to print usage instructions
print_usage() {
  echo "Usage: $0 [dry-run|exec]"
  echo "  dry-run: List old kernels and modules without removing them (default)"
  echo "  exec: Remove the listed old kernels and modules (requires root privileges)"
}

# Function to compare kernel version numbers
# Returns 1 if version1 is greater than version2, 0 if equal, and -1 if lesser
compare_versions() {
  local version1=(${1//./ })
  local version2=(${2//./ })

  for i in {0..2}; do
    if [[ ${version1[i]} -gt ${version2[i]} ]]; then
      return 1
    elif [[ ${version1[i]} -lt ${version2[i]} ]]; then
      return -1
    fi
  done

  return 0
}

# Check for valid input arguments
if [[ $# -gt 1 ]] || { [[ $# -eq 1 ]] && [[ "$1" != "dry-run" ]] && [[ "$1" != "exec" ]]; }; then
  print_usage
  exit 1
fi

# Display current running kernel
uname -a
IN_USE=$(uname -a | awk '{ print $3 }')
echo "Your in-use kernel is $IN_USE"

# Find old kernels
OLD_KERNELS=$(
  dpkg --get-selections |
  grep -v "linux-headers-generic" |
  grep -v "linux-image-generic" |
  grep -Ei 'linux-image|linux-headers|linux-modules' |
  awk '{ print $1 }' |
  grep -v "${IN_USE}"
)

# Filter out newer kernels
FILTERED_KERNELS=""
for kernel in $OLD_KERNELS; do
  kernel_version=$(echo "$kernel" | grep -oP '(?<=linux-image-|linux-headers-|linux-modules-)[0-9]+(\.[0-9]+){0,2}' || true)
  if [[ ! -z "$kernel_version" ]]; then
    compare_versions "$kernel_version" "$IN_USE"
    if [[ $? -eq -1 ]]; then
      FILTERED_KERNELS+="$kernel"$'\n'
    fi
  else
    FILTERED_KERNELS+="$kernel"$'\n'
  fi
done
OLD_KERNELS="$FILTERED_KERNELS"

# Find old modules
OLD_MODULES=$(
  ls /lib/modules |
  grep -v "${IN_USE}" |
  while read -r module; do
    module_version=$(echo "$module" | grep -oP '[0-9]+(\.[0-9]+){0,2}' || true)
    if [[ ! -z "$module_version" ]]; then
      compare_versions "$module_version" "$IN_USE"
      if [[ $? -eq -1 ]]; then
        echo "$module"
      fi
    else
      echo "$module"
    fi
  done
)

# Display old kernels and modules
echo "Old Kernels to be removed:"
echo "$OLD_KERNELS"
echo "Old Modules to be removed:"
echo "$OLD_MODULES"

# Remove old kernels and modules if "exec" argument is passed
if [ "$1" == "exec" ]; then
  # Check for root privileges
  if [ "$(id -u)" != "0" ]; then
    echo "Error:This operation requires root privileges. Please run the script as root or use 'sudo'."
    exit 1
  fi
  # Remove Old Kernel
  apt-get purge $OLD_KERNELS
  # Remove Old Modules
  for module in $OLD_MODULES ; do
    rm -rf /lib/modules/$module/
  done
fi