Bash one-liner para eliminar solo los kernels antiguos

21

He visto muchos subprocesos sobre cómo liberar espacio en la partición / boot y ese es también mi objetivo. Sin embargo, solo me interesa eliminar viejos kernels y no cada uno de ellos sino el actual.

Necesito que la solución sea de una sola línea ya que estaré ejecutando el script de Puppet y no quiero tener archivos adicionales por ahí. Hasta ahora he recibido lo siguiente:

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

Para ser más precisos, lo que hace en este momento es lo siguiente:

  • Listar todos los paquetes linux- * e imprimir sus nombres.
  • Solo enumere los que tienen números y ordénelos, devolviendo el resultado inverso. De esta manera, los núcleos más antiguos se listan al final.
  • Imprime solo los resultados que van después del núcleo actual
  • Ya que hay algunos resultados de linux- {image, headers}, asegúrate de no purgar nada relacionado con mi núcleo actual
  • Llamada apta para purgar

Esto funciona, pero estoy seguro de que la solución puede ser más elegante y segura para un entorno de producción, ya que al menos 20 de nuestros servidores ejecutan Ubuntu.

Gracias por tu tiempo, Alejandro.

    
pregunta Alejandro 07.01.2014 - 15:34

8 respuestas

23

Se ve bastante bien, solo algunos comentarios. Los dos primeros comentarios hacen que el comando sea más seguro, mientras que el tercero y el cuarto lo hacen un poco más corto. Siéntase libre de seguir o ignorar cualquiera de ellos. Aunque os aconsejo encarecidamente seguir los dos primeros. Quieres asegurarte de que sea lo más seguro posible. Es enserio. Estás lanzando un sudo apt-get -y purge en alguna lista de paquetes generada automáticamente. ¡Eso es tan mal ! :)

  1. La lista de todos los linux-* le dará muchos falsos positivos, como (ejemplo de mi salida) linux-sound-base . A pesar de que estos pueden ser filtrados más tarde por el resto de su comando, personalmente me sentiría más seguro de no incluirlos en primer lugar. Controla mejor qué paquetes quieres eliminar. No hagas cosas que puedan tener resultados inesperados. Así que empezaría con

    dpkg -l linux-{image,headers}-*
    
  2. Su expresión regular para "listar solo los que tienen números" es un poco demasiado simple en mi opinión. Por ejemplo, existe el paquete linux-libc-dev:amd64 cuando estás en un sistema de 64 bits. Tu expresión regular coincidirá. No quieres que coincida. Es cierto que si sigues mi primer consejo, entonces linux-libc-dev:amd64 no se incluirá en la lista de todos modos, pero aún así. Sabemos más sobre la estructura de un número de versión que el simple hecho de que "hay un número". Además, generalmente es una buena idea citar las expresiones regulares, solo para evitar posibles interpretaciones erróneas por parte del shell. Así que haría ese comando egrep

     egrep '[0-9]+\.[0-9]+\.[0-9]+'
    
  3. Luego está esta cosa de clasificación. ¿Por qué ordenas? Como de todos modos va a eliminar todos los núcleos (excepto el actual), ¿es importante que elimine los más antiguos antes que los nuevos? No creo que haga ninguna diferencia. ¿O solo está haciendo eso para poder utilizar sed para "Imprimir solo los resultados que van después del núcleo actual"? Pero IMO esto se siente demasiado complicado. ¿Por qué no simplemente filtrar los resultados correspondientes a su kernel actual, como ya está haciendo con grep -v de todos modos, y listo? Honestamente, si tomo la primera parte de su comando (con mis dos sugerencias previas integradas), en mi máquina recibo

    $ 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
    

    Eliminando ese ordenamiento / sed, me sale

    $ 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
    

    Por lo tanto, su comando más complicado realmente perdería dos paquetes en mi máquina, que querría eliminar (ahora es posible que esos linux-image-extra-* thingys dependan del linux-image-* thingys y, por lo tanto, se eliminen de todos modos, pero puede no hace daño hacerlo explícito). En cualquier caso, no veo el punto de su clasificación; un simple grep -v sin preprocesamiento sofisticado debería estar bien, probablemente incluso mejor. Soy un defensor del principio KISS. Le hará más fácil entender o depurar más tarde. Además, sin la clasificación es un poco más eficiente;)

  4. Esto es puramente estético, pero obtendrá la misma salida con esta variante ligeramente más corta. :-)

    $ 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
    

En consecuencia, termino con el comando más simple y seguro

$ 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

Ya que realmente desea limpiar su partición /boot , un enfoque completamente diferente sería listar el contenido de /boot , use dpkg -S para determinar los paquetes a los que pertenecen los archivos individuales, filtre aquellos que pertenezca al núcleo actual, y elimine los paquetes resultantes. Pero me gusta más su enfoque, porque también encontrará paquetes obsoletos como linux-headers-* , que no se instalan en /boot , sino en /usr/src .

    
respondido por el Malte Skoruppa 07.01.2014 - 16:44
5

Escribí esta secuencia de comandos que elimina los paquetes "linux- *" que tienen una versión inferior a la que se inició actualmente. Creo que no es necesario probar el estado del paquete. El comando solicita confirmación antes de purgar los paquetes. Si no quieres eso, agrega la opción -y al comando apt-get.

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

Sin embargo, para poder dejar una cantidad configurable de kernels de repuesto, recomiendo usar mi script linux-purge con la opción --keep . Consulte aquí para obtener más información sobre el script.

    
respondido por el jarno 07.05.2015 - 09:44
3

TL; DR: salta a la parte inferior.

Aunque es un poco más largo. Te lo desglosaré:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}' Al igual que Malte sugirió. Lista los archivos relevantes del kernel.
  2. egrep '[0-9]+\.[0-9]+\.[0-9]+' También sugerido por Malte como la forma más segura de seleccionar solo los archivos del kernel buscando un número de versión.
  3. Dado que ahora posiblemente estamos listando los paquetes de imagen y encabezado, la denominación del paquete puede variar, por lo que tenemos esta solución awk que es necesaria para la clasificación awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}' El resultado es una nueva columna con el número de versión anterior al original nombre del paquete como abajo:

    $ 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. Ahora debemos ordenar la lista para evitar la desinstalación de imágenes más nuevas que la que se está ejecutando actualmente. sort -k1,1 --version-sort -r dándonos esto:

    $ 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. Ahora elimine los archivos de kernel actuales y nuevos sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e 'uname -r | cut -f1,2 -d"-"' dándonos esto:

    $ 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. Ahora quite la primera columna que agregamos con awk '{print $2}' para obtener exactamente lo que queremos:

    $ 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. Ahora podemos enviar eso al administrador de paquetes para que elimine todo automáticamente y reconfigure grub:

    Recomiendo hacer una ejecución en seco primero (aunque para propósitos de scripting, esto podría no ser práctico si tiene un entorno grande)

    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
    

    Ahora, si todo se ve bien, adelante, elimínelo con:

    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
    

Una vez más, el objetivo principal de este "one-liner" es eliminar solo los kernels OLDER que el kernel actualmente en ejecución (lo que deja a todos los kernels recién instalados aún disponibles)

Gracias, avísame cómo funciona esto para ti y si puedes mejorarlo.

    
respondido por el user313760 08.08.2014 - 17:16
1

Puede simplemente listar el directorio / boot para ver las versiones del kernel que tiene usando el comando 'ls'. Luego use 'sudo apt-get -y purge "xxx"' donde "xxx" se reemplaza con el número de versión que desea eliminar. ¡Tenga cuidado de que no sea la versión que está ejecutando actualmente!

    
respondido por el Natarajan 11.09.2016 - 12:57
1

Instala bikeshed ( apt install bikeshed ) y llama a purge-old-kernels como root.

$ sudo purge-old-kernels
    
respondido por el Flow 19.12.2016 - 10:38
0

Una respuesta rápida, explicación a solicitud:

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
    
respondido por el glenn jackman 08.08.2014 - 18:03
0

Me cansé realmente de toda esta complejidad innecesaria y creé un paquete de Python que hace que el trivial de una sola línea:

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

Instálalo con

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

Vea más en enlace .

Espero que esto ayude a otros también.

    
respondido por el mrts 05.10.2015 - 20:54
0
sudo dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)//")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*//;/[0-9]/!d' | xargs sudo apt-get -y purge

Funciona todo el tiempo, e incluso ubuntu 17.10

    
respondido por el David Ramsay 25.04.2018 - 06:03

Lea otras preguntas en las etiquetas