Bash 单行命令仅删除旧内核

Bash 单行命令仅删除旧内核

我看过很多关于如何释放 /boot 分区空间的帖子,这也是我的目标。不过,我只想删除老的内核,而不是每个内核,而是当前的内核。

我需要解决方案是一行代码,因为我将从 Puppet 运行脚本,并且我不想有多余的文件。到目前为止,我得到了以下信息:

dpkg -l linux-* | awk '/^ii/{print $2}' | egrep [0-9] | sort -t- -k3,4 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"` | xargs sudo apt-get -y purge

更准确地说,它目前所做的如下:

  • 列出所有 linux-* 包并打印它们的名称。
  • 仅列出有数字的内核并对其进行排序,返回相反的结果。这样,较旧的内核将列在最后。
  • 仅打印当前内核之后的结果
  • 由于有一些 linux-{image,headers} 结果,请确保我不会清除与当前内核相关的任何内容
  • 调用 apt 来清除

这是可行的,但我确信解决方案可以更优雅并且对于生产环境来说是安全的,因为我们至少有 20 台服务器运行 Ubuntu。

感谢您的时间,亚历杭德罗。

答案1

看起来不错,只有几条注释。前两条注释使命令更安全,而第三和第四条注释使命令更短一些。您可以随意遵循或忽略其中任何一条。不过我强烈建议遵循前两条。您想确保它尽可能安全。我是认真的。您正在sudo apt-get -y purge向一些自动生成的包列表添加一个。这太邪恶的! :)

  1. 列出所有linux-*将得到许多误报,例如(我的输出示例)linux-sound-base。尽管这些可能会在稍后被你的其余命令过滤掉,但我个人认为一开始就不列出它们会更安全。最好控制要删除的软件包。不要做可能会产生意外结果的事情。所以我会从

    dpkg -l linux-{image,headers}-*
    
  2. 我认为,您“仅列出带有数字的版本”的正则表达式有点过于简单。例如,linux-libc-dev:amd64当您在 64 位系统上时,会出现软件包。您的正则表达式将匹配。您不希望它匹配。诚然,如果您遵循了我的第一个建议,那么linux-libc-dev:amd64无论如何都不会被列出,但仍然如此。我们对版本号的结构的了解多于简单的“有一个数字”这一事实。此外,引用正则表达式通常是一个好主意,只是为了防止 shell 可能误解。所以我会制作那个 egrep 命令

     egrep '[0-9]+\.[0-9]+\.[0-9]+'
    
  3. 然后就是排序的事情。为什么要排序?既然你无论如何都要删除所有内核(当前内核除外),那么在删除较旧的内核之前删除较新的内核是否重要?我认为这没什么区别。或者你这样做只是为了可以使用sed“仅打印当前内核之后的结果”?但在我看来,这太复杂了。为什么不简单地过滤掉与当前内核相对应的结果,就像你已经在做的grep -v那样,然后就完成了?老实说,如果我采用你的命令的第一部分(结合我之前的两个建议),在我的计算机上我会得到

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | sort -t- -k3,4 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`
    linux-image-3.8.0-34-generic
    linux-image-3.5.0-44-generic
    

    删除 sorting/sed 内容后,我得到了

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | grep -v -e `uname -r | cut -f1,2 -d"-"`
    linux-image-3.5.0-44-generic
    linux-image-3.8.0-34-generic
    linux-image-extra-3.5.0-44-generic
    linux-image-extra-3.8.0-34-generic
    

    因此,您更复杂的命令实际上会遗漏我机器上的两个包,而我想删除它们(现在有可能那些linux-image-extra-*东西依赖于那些linux-image-*东西,因此无论如何都会被删除,但明确说明这一点也无妨)。无论如何,我不明白您排序的意义;简单的、grep -v没有花哨预处理的应该没问题,甚至可能更好。我是 KISS 原则的支持者。它将使您以后更容易理解或调试。此外,没有排序会稍微更有效率 ;)

  4. 这纯粹是出于美观考虑,但使用这个稍短的变体您将获得相同的输出。:-)

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | grep -v $(uname -r | cut -d- -f-2)
    linux-image-3.5.0-44-generic
    linux-image-3.8.0-34-generic
    linux-image-extra-3.5.0-44-generic
    linux-image-extra-3.8.0-34-generic
    

因此,我最终得到了更简单、更安全的命令

$ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | grep -v $(uname -r | cut -d- -f-2) | xargs sudo apt-get -y purge

由于您确实想要清理/boot分区,因此完全不同的方法是列出 的内容/boot,使用dpkg -S来确定各个文件所属的软件包,过滤掉属于当前内核的软件包,然后删除生成的软件包。但我更喜欢您的方法,因为它还会找到诸如 之类的过时软件包linux-headers-*,这些软件包不会安装到/boot,而是安装到/usr/src

答案2

我编写了这个脚本,用于删除版本低于当前启动版本的“linux-*”软件包。我认为没有必要测试软件包状态。该命令在清除软件包之前要求确认。如果您不想这样做,请在 apt-get 命令中添加 -y 选项。

sudo apt-get purge $(dpkg-query -W -f'${Package}\n' 'linux-*' |
sed -nr 's/.*-([0-9]+(\.[0-9]+){2}-[^-]+).*/\1 &/p' | linux-version sort | 
awk '($1==c){exit} {print $2}' c=$(uname -r | cut -f1,2 -d-))

但是,为了能够保留可配置数量的备用内核,我建议使用linux-purge带有选项的脚本--keep。请参阅这里了解有关脚本的更多信息。

答案3

TL;DR:跳至底部。

不过,它确实有点长。我来给你分解一下:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}'正如 Malte 所建议的。列出相关的内核文件。
  2. egrep '[0-9]+\.[0-9]+\.[0-9]+'Malte 还建议,通过查找版本号来挑选内核文件是一种更安全的方法。
  3. 由于我们现在可能同时列出图像和标题包,包命名可能会有所不同,因此我们有这个 awk 解决方法,这对于排序是必要的,awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}'结果是在原始包名称之前有一个新的列,其中包含版本号,如下所示:

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}'
    3.11.0-23 linux-headers-3.11.0-23
    3.11.0-23 linux-headers-3.11.0-23-generic
    3.11.0-24 linux-headers-3.11.0-24
    3.11.0-24 linux-headers-3.11.0-24-generic
    3.11.0-26 linux-headers-3.11.0-26
    3.11.0-26 linux-headers-3.11.0-26-generic
    3.11.0-23 linux-image-3.11.0-23-generic
    3.11.0-24 linux-image-3.11.0-24-generic
    3.11.0-26 linux-image-3.11.0-26-generic
    3.8.0-35 linux-image-3.8.0-35-generic
    3.11.0-23 linux-image-extra-3.11.0-23-generic
    3.11.0-24 linux-image-extra-3.11.0-24-generic
    3.11.0-26 linux-image-extra-3.11.0-26-generic
    3.8.0-35 linux-image-extra-3.8.0-35-generic
    
  4. 现在我们必须对列表进行排序,以防止卸载任何比当前正在运行的图像更新的图像。sort -k1,1 --version-sort -r给我们这个:

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' | sort -k1,1 --version-sort -r
    3.11.0-26 linux-image-extra-3.11.0-26-generic
    3.11.0-26 linux-image-3.11.0-26-generic
    3.11.0-26 linux-headers-3.11.0-26-generic
    3.11.0-26 linux-headers-3.11.0-26
    3.11.0-24 linux-image-extra-3.11.0-24-generic
    3.11.0-24 linux-image-3.11.0-24-generic
    3.11.0-24 linux-headers-3.11.0-24-generic
    3.11.0-24 linux-headers-3.11.0-24
    3.11.0-23 linux-image-extra-3.11.0-23-generic
    3.11.0-23 linux-image-3.11.0-23-generic
    3.11.0-23 linux-headers-3.11.0-23-generic
    3.11.0-23 linux-headers-3.11.0-23
    3.8.0-35 linux-image-extra-3.8.0-35-generic
    3.8.0-35 linux-image-3.8.0-35-generic
    
  5. 现在删除当前和较新的内核文件,sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`得到以下结果:

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' | sort -k1,1 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`
    3.11.0-23 linux-image-extra-3.11.0-23-generic
    3.11.0-23 linux-image-3.11.0-23-generic
    3.11.0-23 linux-headers-3.11.0-23-generic
    3.11.0-23 linux-headers-3.11.0-23
    3.8.0-35 linux-image-extra-3.8.0-35-generic
    3.8.0-35 linux-image-3.8.0-35-generic
    
  6. 现在去掉我们添加的第一列awk '{print $2}'以获得我们想要的内容:

    $ dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' | sort -k1,1 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"` | awk '{print $2}'
    linux-image-extra-3.11.0-23-generic
    linux-image-3.11.0-23-generic
    linux-headers-3.11.0-23-generic
    linux-headers-3.11.0-23
    linux-image-extra-3.8.0-35-generic
    linux-image-3.8.0-35-generic
    
  7. 现在我们可以将其提供给包管理器以自动删除所有内容并重新配置 grub:

    我建议先进行一次试运行(尽管对于你的脚本目的来说,如果你的环境很大,这可能不切实际)

    dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' | sort -k1,1 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"` | awk '{print $2}' | xargs sudo apt-get --dry-run remove
    

    现在,如果一切看起来都不错,请继续使用以下命令将其删除:

    dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' | egrep '[0-9]+\.[0-9]+\.[0-9]+' | awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' | sort -k1,1 --version-sort -r | sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"` | awk '{print $2}' | xargs sudo apt-get -y purge
    

再次强调,这句“一行”命令的重点是只删除比当前正在运行的内核更旧的内核(这使得任何新安装的内核仍然可用)

谢谢,让我知道这对您有何作用以及您是否可以改进它!

答案4

您可以使用“ls”命令简单地列出 /boot 目录以查看您拥有的内核版本。然后使用“sudo apt-get -y purge "xxx"”,其中“xxx”替换为您要删除的版本号。注意它不是您当前正在运行的版本!!

相关内容