Bash one-liner pour supprimer uniquement les anciens noyaux

23

J'ai vu beaucoup de discussions sur la façon de libérer de l'espace sur la partition / boot et c'est aussi mon objectif. Cependant, je ne souhaite supprimer que les anciens noyaux et non chacun d'entre eux, mais l'actuel.

J'ai besoin que la solution soit une ligne car je vais exécuter le script à partir de Puppet et je ne veux pas avoir de fichiers supplémentaires. Jusqu'à présent, j'ai obtenu ce qui suit:

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

Pour être plus précis, ce qu'il fait en ce moment est le suivant:

  • Répertoriez tous les packages linux- * et imprimez leurs noms.
  • Répertoriez uniquement ceux qui ont des numéros et triez-les, en retournant le résultat inverse. De cette façon, les noyaux plus anciens sont répertoriés en dernier.
  • Imprimer uniquement les résultats qui vont après le noyau actuel
  • Puisqu'il y a des résultats linux- {image, headers}, assurez-vous que je ne purgerai rien lié à mon noyau actuel
  • Appel apte à purger

Cela fonctionne, mais je suis sûr que la solution peut être plus élégante et qu'elle est sûre pour un environnement de production, car au moins 20 de nos serveurs exécutent Ubuntu.

Merci pour votre temps, Alejandro.

Alejandro
la source
Quelque chose de vieux sur le Net: tuxtweaks.com/2010/10/…
user68186
Comment utilisez-vous votre one-liner, s'il n'est pas enregistré dans un fichier script?
jarno

Réponses:

25

Ça a l'air sympa, juste quelques commentaires. Les deux premiers commentaires rendent la commande plus sûre, tandis que les troisième et quatrième la raccourcissent un peu. N'hésitez pas à suivre ou à ignorer l'un d'eux. Bien que je vous conseille fortement de suivre les deux premiers. Vous voulez vous assurer que c'est aussi sûr que possible. Je veux dire sérieusement. Vous lancez une sudo apt-get -y purgeliste de packages générée automatiquement. C'est tellement mal ! :)

  1. En listant tout linux-*, vous obtiendrez de nombreux faux positifs, tels que (exemple de ma sortie) linux-sound-base. Même si ceux-ci peuvent être filtrés plus tard par le reste de votre commande, je me sentirais personnellement plus en sécurité de ne pas les répertorier en premier lieu. Mieux contrôler les packages que vous souhaitez supprimer. Ne faites pas de choses qui pourraient avoir des résultats inattendus. Je commencerais donc par

    dpkg -l linux-{image,headers}-*
  2. Votre expression rationnelle pour «ne lister que ceux qui ont des chiffres» est un peu trop simple à mon avis. Par exemple, il y a le package linux-libc-dev:amd64lorsque vous êtes sur un système 64 bits. Votre expression régulière correspondra. Vous ne voulez pas qu'elle corresponde. Certes, si vous avez suivi mon premier conseil, linux-libc-dev:amd64vous ne serez pas répertorié de toute façon, mais quand même. Nous en savons plus sur la structure d'un numéro de version que sur le simple fait "il y a un numéro". De plus, c'est généralement une bonne idée de citer des expressions rationnelles, juste pour éviter d'éventuelles interprétations erronées par le shell. Je ferais donc cette commande egrep

     egrep '[0-9]+\.[0-9]+\.[0-9]+'
  3. Ensuite, il y a ce tri. Pourquoi triez-vous? Puisque vous allez de toute façon supprimer tous les noyaux (sauf celui actuel), est-il important pour vous de supprimer les plus anciens avant les plus récents? Je ne pense pas que cela fasse une différence. Ou faites-vous cela uniquement pour pouvoir ensuite sed"Imprimer uniquement les résultats qui vont après le noyau actuel"? Mais l'OMI, cela semble beaucoup trop compliqué. Pourquoi ne pas simplement filtrer les résultats correspondant à votre noyau actuel, comme vous le faites déjà de grep -vtoute façon, et finir? Honnêtement, si je prends la première partie de votre commande (avec mes deux suggestions précédentes intégrées), sur ma machine je reçois

    $ 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
    

    En supprimant ces trucs de tri / sed, je reçois

    $ 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
    

    Donc, votre commande plus compliquée manquerait en fait deux packages sur ma machine, que je voudrais supprimer (maintenant il est possible que ces linux-image-extra-*thingys dépendent des linux-image-*thingys et soient donc supprimés de toute façon, mais cela ne peut pas nuire à le rendre explicite). En tout cas, je ne vois pas l'intérêt de votre tri; un grep -vprétraitement simple sans fantaisie devrait être bien, probablement encore mieux. Je suis un partisan du principe KISS. Cela vous facilitera la compréhension ou le débogage plus tard. De plus, sans le tri, c'est un peu plus efficace;)

  4. C'est purement esthétique mais vous obtiendrez la même sortie avec cette variante légèrement plus courte. :-)

    $ 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
    

Par conséquent, je me retrouve avec la commande plus simple et plus sûre

$ 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

Puisque vous voulez réellement nettoyer votre /bootpartition, une approche complètement différente serait de lister le contenu de /boot, utiliser dpkg -Spour déterminer les packages auxquels les fichiers individuels appartiennent, filtrer ceux qui appartiennent au noyau actuel et supprimer les packages résultants. Mais j'aime mieux votre approche, car elle trouvera également des packages obsolètes tels que linux-headers-*, qui ne sont pas installés dans /boot, mais dans /usr/src.

Malte Skoruppa
la source
Merci pour votre réponse @Malte, merci pour votre contribution. Je trouve vos deux premières étapes très clarifiantes et elles rendent le oneliner plus sûr. Cependant, je crois que vos deux dernières étapes ignorent les noyaux les plus récents et les purgent également. Au moins, j'ai essayé votre solution sur un de mes serveurs et elle aurait désinstallé quelque chose de indésirable.
Alejandro
C'est intéressant. Pouvez-vous me PM les deux sorties, une avec la commande avec ma troisième et quatrième suggestion inclus, une sans eux? Plus la sortie de uname -r. Pour moi, ça fonctionne bien. Il serait intéressant de voir pourquoi cela ne fonctionne pas dans votre cas.
Malte Skoruppa
1
J'ai essayé de vous envoyer un message mais je n'ai trouvé aucune adresse e-mail, donc je posterai ma sortie ici: hastebin.com/raleyigowe.vhdl uname -r montre que mon serveur utilise linux-image-3.8.0-34 Le truc c'est que parfois un serveur a téléchargé un noyau plus récent mais il ne l'a pas encore installé. C'est pourquoi je cherchais un moyen de supprimer uniquement les vieux noyaux.
Alejandro
Cette commande a sélectionné le fichier d'en-tête pour mon noyau actuel, ce qui semble indésirable.
Mark Stosberg
1
@MalteSkoruppa, uname -rproduit 4.8.0-36-generic , qui ne parvient pas à exclure linux-headers-4.8.0-36de la liste.
Mark Stosberg
7

J'ai écrit ce script qui supprime les paquets "linux- *" qui ont une version inférieure à celle actuellement démarrée. Je pense qu'il n'est pas nécessaire de tester l'état du package. La commande demande une confirmation avant de purger les packages. Si vous ne le souhaitez pas, ajoutez l'option -y à la commande 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-))

Cependant, pour pouvoir laisser une quantité configurable de noyaux de rechange, je recommande d'utiliser mon linux-purgescript avec --keepoption. Voir ici pour plus d'informations sur le script.

jarno
la source
3

TL; DR: sauter vers le bas.

C'est un peu plus long cependant. Je vais le décomposer pour vous:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}'Tout comme Malte l'a suggéré. Répertorie les fichiers du noyau pertinents.
  2. egrep '[0-9]+\.[0-9]+\.[0-9]+' Malte a également suggéré que le moyen le plus sûr de sélectionner uniquement les fichiers du noyau en recherchant un numéro de version.
  3. Étant donné que nous répertorions maintenant à la fois l'image et les packages d'en-tête, la dénomination du package peut varier.Nous avons donc cette solution de contournement awk qui est nécessaire pour le tri.Le awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}'résultat est une nouvelle colonne avec le numéro de version avant le nom du package d'origine, comme ci-dessous:

    $ 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. Maintenant, nous devons trier la liste afin d'éviter de désinstaller des images plus récentes que celle en cours d'exécution. sort -k1,1 --version-sort -rnous donnant ceci:

    $ 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. Maintenant, supprimez les fichiers du noyau actuels et plus récents sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`en nous donnant ceci:

    $ 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. Maintenant, supprimez la première colonne que nous avons ajoutée awk '{print $2}'pour obtenir exactement ce que nous voulons:

    $ 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. Maintenant, nous pouvons envoyer cela au gestionnaire de paquets pour supprimer automatiquement tout et reconfigurer grub:

    Je recommande de faire d'abord un essai à sec (bien que pour vos besoins de script, cela puisse ne pas être pratique si vous avez un grand environnement)

    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

    Maintenant, si tout va bien, allez-y et supprimez-le avec:

    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

Encore une fois, le but de ce "one-liner" est de supprimer uniquement les noyaux PLUS ANCIENS que le noyau en cours d'exécution (ce qui laisse les noyaux nouvellement installés encore disponibles)

Merci de me faire savoir comment cela fonctionne pour vous et si vous pouvez l'améliorer!

user313760
la source
Vérifiez ma réponse
jarno
1

Vous pouvez simplement lister le répertoire / boot pour voir les versions du noyau que vous avez en utilisant la commande 'ls'. Utilisez ensuite 'sudo apt-get -y purge "xxx"' où "xxx" est remplacé par le numéro de version que vous souhaitez supprimer. Attention, ce n'est pas la version que vous utilisez actuellement !!.

Natarajan
la source
1
Ils voulaient une commande à une ligne. Votre solution nécessite plus d'une ligne
Anwar
1

Install bikeshed( apt install bikeshed) et appelez en purge-old-kernelstant que root.

$ sudo purge-old-kernels
Couler
la source
purge-old-kernels est déconseillé au profit de "apt autoremove". Si vous avez des problèmes, veuillez classer les bogues contre apt. Voir bugs.launchpad.net/bikeshed/+bug/1569228/comments/7
Amedee Van Gasse
0

Une réponse rapide, explication sur demande:

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
la source
2
Je suggère (ou, si vous préférez, demandez) de l'étendre pour inclure l'explication. :)
Eliah Kagan
@EliahKagan, je pense que le script n'a pas de sens. Pourquoi ignorer les packages dont l'installation n'est pas souhaitée? Le script peut supprimer des noyaux plus récents que le noyau actuel ou en enregistrer d'autres plus anciens par sed '$d'commande. Le script ne supprime aucun package d'en-tête linux ni aucun autre package lié aux packages du noyau à supprimer, en plus il ne supprime pas les fichiers de configuration des packages. Je recommanderais plutôt d'utiliser ma réponse.
jarno
@EliahKagan En fait, le script ne supprime aucun paquet, mais affiche (par echo) la apt-getcommande que vous pourriez exécuter.
jarno
0

Je me suis vraiment fatigué de toute cette complexité inutile et j'ai créé un package Python qui rend le one-liner trivial:

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

Installez-le avec

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

Voir plus sur https://github.com/mrts/ubuntu-old-kernel-cleanup .

J'espère que cela aide aussi les autres.

mrts
la source
J'ai essayé ceci mais j'obtiens une erreur: "ubuntu-old-kernel-cleanup: commande introuvable"
Grant
Ensuite, il ne se trouve pas dans votre chemin de recherche exécutable. L'avez-vous installé avec sudo pip install ...comme décrit ci-dessus? sudoest en fait important, sinon pipl'installera dans un répertoire utilisateur et le shell ne pourra pas rechercher dans ce répertoire les fichiers exécutables.
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

Fonctionne tout le temps, et même Ubuntu 17.10

David Ramsay
la source
Pour moi, cela essaie de supprimer linux-libc-dev:amd64, c'est indésirable.
Malte Skoruppa