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
в некотором автоматически сгенерированном списке пакетов. Это так зло!:)
Перечисление всех
linux-*
получит много ложных срабатываний, например (пример из моего вывода)linux-sound-base
, Хотя они могут быть отфильтрованы позже остальной вашей командой, я бы лично чувствовал себя безопаснее, не перечисляя их в первую очередь. Лучше контролировать, какие пакеты вы хотите удалить. Не делайте вещи, которые могут иметь неожиданные результаты. Так что я бы начал сdpkg -l linux-{image,headers}-*
На мой взгляд, ваше регулярное выражение "перечислять только те, у которых есть цифры", слишком простое. Например, есть пакет
linux-libc-dev:amd64
когда вы в 64-битной системе. Ваше регулярное выражение будет соответствовать. Вы не хотите, чтобы это соответствовало. Правда, если вы следовали моему первому совету, тоlinux-libc-dev:amd64
в любом случае не будет в списке, но все же. Мы знаем больше о структуре номера версии, чем простой факт "есть число". Кроме того, как правило, рекомендуется заключать в кавычки регулярные выражения, просто чтобы предотвратить возможные неверные интерпретации оболочкой. Так что я бы сделал эту команду egrepegrep '[0-9]+\.[0-9]+\.[0-9]+'
Тогда есть эта вещь сортировки. Почему вы сортируете? Так как вы собираетесь удалить все ядра (кроме текущего) в любом случае, важно ли удалять старые, прежде чем новые? Я не думаю, что это имеет значение. Или вы делаете это только для того, чтобы потом использовать
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. Это облегчит вам понимание или отладку позже. Кроме того, без сортировки это немного более эффективно;)Это чисто эстетично, но вы получите тот же результат с этим немного более коротким вариантом.:-)
$ 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: пропустить на дно.
Это немного дольше, хотя. Я сломаю это для вас:
dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}'
Так же, как предложил Малте. Перечисляет соответствующие файлы ядра.egrep '[0-9]+\.[0-9]+\.[0-9]+'
Малте также предложил в качестве более безопасного способа выбрать только файлы ядра путем поиска номера версии.Поскольку теперь мы, возможно, перечисляем как пакеты изображений, так и пакеты заголовков, имена пакетов могут различаться, поэтому у нас есть этот обходной путь 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
Теперь мы должны отсортировать список, чтобы предотвратить удаление более новых образов, чем тот, который запущен в данный момент.
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
Теперь удалите текущие и более новые файлы ядра
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
Теперь удалите первый столбец, который мы добавили
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
Теперь мы можем передать это менеджеру пакетов, чтобы автоматически удалить все и перенастроить 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