Bash one-liner para eliminar solo los núcleos antiguos

23

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

Necesito que la solución sea de una sola línea ya que ejecutaré el script desde Puppet y no quiero tener archivos adicionales por ahí. Hasta ahora obtuve 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:

  • Liste todos los paquetes linux- * e imprima 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 enumeran al final.
  • Imprima solo los resultados que van después del núcleo actual
  • Dado que hay algunos resultados de Linux- {imagen, encabezados}, asegúrese de no purgar nada relacionado con mi núcleo actual
  • Llamar apto 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.

Alejandro
fuente
Algo viejo de la red: tuxtweaks.com/2010/10/…
user68186
¿Cómo utiliza su one-liner, si no está guardado en un archivo de script?
jarno

Respuestas:

25

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 a cualquiera de ellos. Aunque les aconsejaré que sigan los dos primeros. Debes asegurarte de que sea lo más seguro posible. Es enserio. Estás lanzando una sudo apt-get -y purgelista de paquetes generada automáticamente. Eso es tan malo ! :)

  1. Listado de todos linux-*obtendrá muchos falsos positivos, como (ejemplo de mi salida) linux-sound-base. Aunque estos pueden ser filtrados más tarde por el resto de su comando, personalmente me sentiría más seguro si no los enumero en primer lugar. Controle mejor qué paquetes desea eliminar. No haga cosas que puedan tener resultados inesperados. Entonces comenzaría con

    dpkg -l linux-{image,headers}-*
  2. Su expresión regular para "enumerar solo los que tienen números" es un poco demasiado simple en mi opinión. Por ejemplo, existe el paquete linux-libc-dev:amd64cuando estás en un sistema de 64 bits. Tu expresión regular coincidirá. No quieres que coincida. Es cierto que si seguiste mi primer consejo, de linux-libc-dev:amd64todos modos no apareceré en la lista, 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 expresiones regulares, solo para evitar posibles interpretaciones erróneas por parte del shell. Entonces haría ese comando egrep

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

    $ 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 material de clasificación / sed, obtengo

    $ 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

    Entonces, su comando más complicado en realidad perdería dos paquetes en mi máquina, que me gustaría eliminar (ahora es posible que esas linux-image-extra-*cosas dependan de las linux-image-*cosas y, por lo tanto, se eliminen de todos modos, pero no puede hacer daño hacerlo explícito). En cualquier caso, no veo el punto de su clasificación; un grep -vpreprocesamiento sencillo y elegante debería estar bien, presumiblemente incluso mejor. Soy un defensor del principio KISS. Le facilitará la comprensión o la depuración más adelante. 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

Dado que realmente desea limpiar su /bootpartición, un enfoque completamente diferente sería enumerar los contenidos /boot, utilizarlos dpkg -Spara determinar los paquetes a los que pertenecen los archivos individuales, filtrar los que pertenecen al núcleo actual y eliminar los paquetes resultantes. Pero me gusta más su enfoque, porque también encontrará paquetes obsoletos como linux-headers-*, por ejemplo , que no se instalan /boot, sino a /usr/src.

Malte Skoruppa
fuente
Gracias por su respuesta @Malte, gracias por su aporte. Encuentro tus dos primeros pasos muy claros y hacen que el oneliner sea más seguro. Sin embargo, creo que sus dos últimos pasos ignoran los núcleos más nuevos y también los purgan. Al menos, probé su solución en un servidor mío y habría desinstalado algo no deseado.
Alejandro
Eso es interesante. ¿Me puede enviar las dos salidas, una con el comando con mi tercera y cuarta sugerencia incluidas, una sin ellas? Además de la salida de uname -r. Para mi funciona bien. Sería interesante ver por qué no funciona en su caso.
Malte Skoruppa
1
Traté de enviarle un mensaje de correo electrónico, pero no encontré ninguna dirección de correo electrónico, así que publicaré mi salida aquí: hastebin.com/raleyigowe.vhdl uname -r muestra que mi servidor está usando linux-image-3.8.0-34 La cosa es que a veces un servidor ha descargado un kernel más nuevo pero aún no lo ha instalado. Es por eso que estaba buscando una manera de eliminar solo los núcleos antiguos.
Alejandro
Este comando seleccionó el archivo de encabezado para mi núcleo actual, lo que parece indeseable.
Mark Stosberg
1
@MalteSkoruppa, uname -rproduce 4.8.0-36-generic , que no puede excluir linux-headers-4.8.0-36de la lista.
Mark Stosberg
7

Escribí este script que elimina los paquetes "linux- *" que tienen una versión menor que la que actualmente arranca. Creo que no es necesario probar el estado del paquete. El comando solicita confirmación antes de purgar paquetes. Si no quiere eso, agregue 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}-[^-]+).*/\1 &/p' | linux-version sort | 
awk '($1==c){exit} {print $2}' c=$(uname -r | cut -f1,2 -d-))

Sin embargo, para poder dejar una cantidad configurable de núcleos de repuesto, recomiendo usar mi linux-purgescript con --keepopción. Consulte aquí para obtener más información sobre el script.

jarno
fuente
3

TL; DR: saltar al final.

Aunque es un poco más largo. Lo desglosaré por ti:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}'Justo como Malte sugirió. Enumera los archivos de kernel relevantes.
  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 núcleo buscando un número de versión.
  3. Dado que ahora posiblemente estamos enumerando tanto la imagen como los paquetes de encabezado, el nombre del paquete puede variar, por lo que tenemos esta solución alternativa 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 antes del nombre del paquete original, como se muestra a continuación:

    $ 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 -rdá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 más nuevos sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`que nos dan 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 elimine la primera columna que agregamos 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 eliminar automáticamente todo y volver a configurar grub:

    Recomiendo hacer una ejecución en seco primero (aunque para sus propósitos de script 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 y quítelo 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 de este "one-liner" es eliminar solo los núcleos MÁS ANTIGUOS que el núcleo que se está ejecutando actualmente (lo que deja todos los núcleos recién instalados todavía disponibles)

¡Gracias, hágame saber cómo funciona esto para usted y si puede mejorarlo!

usuario313760
fuente
Comprueba mi respuesta
jarno
1

Simplemente puede enumerar el directorio / boot para ver las versiones del núcleo 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.

Natarajan
fuente
1
Querían un comando de una sola línea. Su solución necesita más de 1 línea
Anwar
1

Instalar bikeshed( apt install bikeshed) y llamar purge-old-kernelscomo root.

$ sudo purge-old-kernels
Fluir
fuente
purge-old-kernels ha quedado en desuso, a favor de "apt autoremove". Si tiene problemas, presente los errores en apt. Ver bugs.launchpad.net/bikeshed/+bug/1569228/comments/7
Amedee Van Gasse
0

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

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
Glenn Jackman
fuente
2
Sugiero (o, si lo prefiere, solicitar) expandir esto para incluir la explicación. :)
Eliah Kagan
@EliahKagan, creo que el guión no tiene sentido. ¿Por qué omitir paquetes que no se desean instalar? El script puede eliminar algunos núcleos más nuevos que el actual, o guardar algunos más antiguos por sed '$d'comando. El script no elimina ningún paquete de encabezado de Linux u otros paquetes relacionados con los paquetes del núcleo que se eliminarán, además no elimina los archivos de configuración de los paquetes. En su lugar, recomendaría usar mi respuesta.
jarno
@EliahKagan Actally, el script no elimina ningún paquete, pero imprime (por echo) el apt-getcomando que puede ejecutar.
jarno
0

Me cansé realmente de toda esta complejidad innecesaria y creé un paquete de Python que hace que la línea simple sea trivial:

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

Instalarlo con

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

Ver más en https://github.com/mrts/ubuntu-old-kernel-cleanup .

Espero que esto ayude a otros también.

mrts
fuente
Intenté esto pero recibo el error: "ubuntu-old-kernel-cleanup: comando no encontrado"
Grant
Entonces no está en su ruta de búsqueda ejecutable. ¿Lo instaló sudo pip install ...como se describe anteriormente? sudoes realmente importante, de lo contrario piplo instalará en algún directorio de usuarios y es posible que Shell no busque en este directorio archivos ejecutables.
mrts
0
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

Funciona todo el tiempo, e incluso ubuntu 17.10

David Ramsay
fuente
Para mí esto intenta eliminar linux-libc-dev:amd64, eso no es deseado.
Malte Skoruppa