Bash one-liner для удаления только старых ядер

Я видел много тем о том, как освободить место в разделе / ​​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}, убедитесь, что я не буду очищать что-либо, связанное с моим текущим ядром
  • Позвоните, чтобы очистить

Это работает, но я уверен, что решение может быть более элегантным и безопасным для производственной среды, так как по крайней мере 20 наших серверов работают под управлением Ubuntu.

Спасибо за ваше время, Алехандро.

9 ответов

Выглядит достаточно красиво, всего несколько комментариев. Два первых комментария делают команду более безопасной, а третий и четвертый делают ее немного короче. Не стесняйтесь следовать или игнорировать любой из них. Хотя я настоятельно советую следовать первым двум. Вы хотите убедиться, что это максимально безопасно. Я имею в виду серьезно. Ты бросаешь 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 в любом случае не будет в списке, но все же. Мы знаем больше о структуре номера версии, чем простой факт "есть число". Кроме того, как правило, рекомендуется заключать в кавычки регулярные выражения, просто чтобы предотвратить возможные неверные интерпретации оболочкой. Так что я бы сделал эту команду 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
    

    Убирая сортировку / сборку, я получаю

    $ 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,

Я написал этот скрипт, который удаляет пакеты "linux-*", которые имеют меньшую версию, чем загруженная в данный момент. Я думаю, что нет необходимости проверять состояние пакета. Команда запрашивает подтверждение перед очисткой пакетов. Если вы не хотите этого, добавьте опцию -y в команду apt-get.

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

Однако, чтобы иметь возможность оставлять настраиваемое количество запасных ядер, я рекомендую использовать мой linux-purge сценарий с --keep вариант. Смотрите здесь для получения дополнительной информации о сценарии.

TL;DR: пропустить на дно.

Это немного дольше, хотя. Я сломаю это для вас:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' Так же, как предложил Малте. Перечисляет соответствующие файлы ядра.
  2. egrep '[0-9]+\.[0-9]+\.[0-9]+' Малте также предложил в качестве более безопасного способа выбрать только файлы ядра путем поиска номера версии.
  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
    

Еще раз, весь смысл этого "однострочного" заключается в том, чтобы удалить только те ядра, которые были старше, чем работающее в настоящее время ядро ​​(что оставляет все новые установленные ядра доступными).

Спасибо, дайте мне знать, как это работает для вас, и если вы могли бы улучшить его!

Я действительно устал от всей этой ненужной сложности и создал пакет Python, который делает тривиальную строку:

ubuntu-old-kernel-cleanup | xargs sudo apt-get -y purge

Установите его с

sudo pip install git+http://github.com/mrts/ubuntu-old-kernel-cleanup.git

Смотрите больше на https://github.com/mrts/ubuntu-old-kernel-cleanup.

Надеюсь, что это помогает другим.

Вы можете просто перечислить каталог /boot, чтобы увидеть имеющиеся у вас версии ядра, используя команду 'ls'. Затем используйте 'sudo apt-get -y purge "xxx", где "xxx" заменяется номером версии, которую вы хотите удалить. Позаботьтесь, чтобы это была не та версия, которую вы сейчас используете!!

Устанавливать bikeshed (apt install bikeshed) и позвоните purge-old-kernels как корень.

$ sudo purge-old-kernels

Эта однострочная команда отлично работает.

Если вы хотите, вы всегда можете удалить опцию -y , чтобы лучше проверить результаты перед применением изменений.

      # purge old kernels in one-line command

sudo apt purge -y $(sudo apt list linux-image* 2>/dev/null|grep instal|head -n -2|cut -d/ -f1) $(sudo apt list linux-headers* 2>/dev/null|grep instal|head -n -3|cut -d/ -f1)

Для получения нужного результата я разделил поиск linux-image и linux-headers .

Команда 2>/dev/null удалила предупреждения.

Слово instal , используемое в grep , подходит как минимум для трех разных языков: итальянского (installato), английского (установлено) и испанского (instalado).

Я использовал head для исключения текущего ядра.

С помощью cut -d/ -f1 мы получаем полное имя пакета.

Я надеюсь, что эта информация поможет многим людям, использующим Linux.

sudo dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d' | xargs sudo apt-get -y purge

Работает все время, и даже Ubuntu 17.10

Быстрый ответ, объяснение по запросу:

dpkg -l 'linux-image-[0-9]*' | 
awk -v current="$(uname -r)" '!/^i/ || $2~current {next} {print $2}' |
sed '$d' | 
xargs echo sudo apt-get autoremove
Другие вопросы по тегам