Bash one-liner para excluir apenas kernels antigos

23

Já vi muitos tópicos sobre como liberar espaço na partição / boot e esse é meu objetivo também. No entanto, só estou interessado em excluir kernels antigos e não cada um deles, mas o atual.

Preciso que a solução seja de uma linha, pois executarei o script do Puppet e não quero ter arquivos extras por aí. Até agora, consegui o seguinte:

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 mais preciso, o que faz no momento é o seguinte:

  • Liste todos os pacotes linux- * e imprima seus nomes.
  • Liste apenas os que têm números e os ordene, retornando o resultado inverso. Dessa forma, os kernels mais antigos são listados por último.
  • Imprima apenas os resultados que seguem o kernel atual
  • Como existem alguns resultados de linux- {image, headers}, certifique-se de que eu não limpe nada relacionado ao meu kernel atual
  • Ligue para o apt para limpar

Isso funciona, mas tenho certeza de que a solução pode ser mais elegante e segura para um ambiente de produção, pois pelo menos 20 de nossos servidores executam o Ubuntu.

Obrigado pelo seu tempo, Alejandro.

Alejandro
fonte
Algo antigo da rede: tuxtweaks.com/2010/10/…
user68186
Como você usa seu one-liner, se ele não é salvo em um arquivo de script?
Jarno

Respostas:

25

Parece bom o suficiente, apenas alguns comentários. Os dois primeiros comentários tornam o comando mais seguro, enquanto o terceiro e o quarto o tornam um pouco mais curto. Sinta-se livre para seguir ou ignorar qualquer um deles. Embora eu recomendo fortemente seguir os dois primeiros. Você quer ter certeza de que é o mais seguro possível. Quero dizer a sério. Você está lançando uma sudo apt-get -y purgeem alguma lista de pacotes gerada automaticamente. Isso é tão mau ! :)

  1. A listagem de todos linux-*fornece muitos falsos positivos, como (exemplo da minha saída) linux-sound-base. Mesmo que estes possam ser filtrados posteriormente pelo resto do seu comando, eu me sentiria mais seguro em não listá-los em primeiro lugar. Controle melhor quais pacotes você deseja remover. Não faça coisas que possam ter resultados inesperados. Então eu começaria com

    dpkg -l linux-{image,headers}-*
  2. Seu regex para "listar apenas os que têm números" é um pouco simples demais na minha opinião. Por exemplo, existe o pacote linux-libc-dev:amd64quando você está em um sistema de 64 bits. Seu regex corresponderá. Você não quer que isso corresponda. É certo que, se você seguiu meu primeiro conselho, linux-libc-dev:amd64não será listado de qualquer maneira, mas ainda assim. Sabemos mais sobre a estrutura de um número de versão do que o simples fato de que "há um número". Além disso, geralmente é uma boa idéia citar regexes, apenas para evitar possíveis interpretações erradas pelo shell. Então eu faria esse comando egrep

     egrep '[0-9]+\.[0-9]+\.[0-9]+'
  3. Depois, há essa coisa de classificação. Por que você classifica? Como você removerá todos os núcleos (exceto o atual), é importante remover os mais antigos antes dos mais novos? Eu não acho que isso faça alguma diferença. Ou você está apenas fazendo isso para poder usar sed"Imprimir apenas os resultados que seguem o kernel atual"? Mas na IMO isso parece muito complicado. Por que não simplesmente filtrar os resultados correspondentes ao seu kernel atual, como você já está fazendo de grep -vqualquer maneira, e pronto? Honestamente, se eu pegar a primeira parte do seu comando (com minhas duas sugestões anteriores integradas), na minha máquina eu recebo

    $ 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

    Removendo essas coisas de classificação / sed, eu recebo

    $ 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

    Portanto, seu comando mais complicado realmente perderia dois pacotes na minha máquina, que eu gostaria de remover (agora é possível que essas linux-image-extra-*coisas dependam das linux-image-*coisas e, portanto, sejam removidas de qualquer maneira, mas não custa nada explicá-las). De qualquer forma, não vejo o objetivo da sua classificação; um simples grep -vsem pré-processamento sofisticado deve ser bom, presumivelmente ainda melhor. Eu sou um defensor do princípio do KISS. Isso facilitará a compreensão ou a depuração posteriormente. Além disso, sem a classificação, é um pouco mais eficiente;)

  4. Isso é puramente estético, mas você obterá a mesma saída com essa variante um pouco mais curta. :-)

    $ 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

Consequentemente, acabo com o comando mais simples e 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

Como você realmente deseja limpar sua /bootpartição, uma abordagem completamente diferente seria listar o conteúdo /boot, usar dpkg -Spara determinar os pacotes aos quais os arquivos individuais pertencem, filtrar os que pertencem ao kernel atual e remover os pacotes resultantes. Mas eu gosto mais da sua abordagem, porque ela também encontrará pacotes desatualizados, como os linux-headers-*quais não são instalados /boot, mas sim /usr/src.

Malte Skoruppa
fonte
Obrigado pela sua resposta @ Malte, obrigado pela sua contribuição. Acho seus dois primeiros passos muito esclarecedores e tornam o oneliner mais seguro. No entanto, acredito que seus dois últimos passos ignoram os kernels mais novos e os limpam também. Pelo menos, tentei sua solução em um servidor meu e ele teria desinstalado algo indesejado.
Alejandro
Isso é interessante. Você pode me dar as duas saídas, uma com o comando com minha terceira e quarta sugestão incluídas, uma sem elas? Além da saída de uname -r. Para mim, funciona bem. Seria interessante ver por que isso não funciona no seu caso.
Malte Skoruppa
1
Eu tentei fazer o PM, mas não encontrei nenhum endereço de e-mail, então postarei minha saída aqui: haste : [email protected] uname -r mostra que meu servidor está usando linux-image-3.8.0-34 O problema é que Às vezes, um servidor baixou um kernel mais recente, mas ainda não o instalou. É por isso que eu estava procurando uma maneira de remover apenas kernels antigos.
Alejandro
Este comando selecionou o arquivo de cabeçalho para o meu kernel atual, o que parece indesejável.
Mark Stosberg
1
@MalteSkoruppa, uname -rproduz 4.8.0-36-generic , que falha ao excluir linux-headers-4.8.0-36da lista.
Mark Stosberg
7

Eu escrevi este script que remove os pacotes "linux- *" que possuem uma versão menor do que a atualmente inicializada. Eu acho que não é necessário testar o status do pacote. O comando pede confirmação antes de limpar os pacotes. Se você não quer isso, adicione a opção -y ao 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-))

No entanto, para poder deixar uma quantidade configurável de kernels sobressalentes, recomendo usar meu linux-purgescript com a --keepopção Veja aqui para mais informações sobre o script.

jarno
fonte
3

TL; DR: pule para o fundo.

É um pouco mais longo. Vou dividir para você:

  1. dpkg -l linux-{image,headers}-* | awk '/^ii/{print $2}'Assim como Malte sugeriu. Lista os arquivos relevantes do kernel.
  2. egrep '[0-9]+\.[0-9]+\.[0-9]+' Também sugerido por Malte como a maneira mais segura de escolher apenas os arquivos do kernel, procurando por um número de versão.
  3. Como agora estamos possivelmente listando os pacotes de imagem e de cabeçalho, a nomeação dos pacotes pode variar, de modo que temos essa solução alternativa do awk necessária para a classificação awk 'BEGIN{FS="-"}; {if ($3 ~ /[0-9]+/) print $3"-"$4,$0; else if ($4 ~ /[0-9]+/) print $4"-"$5,$0}'O resultado é uma nova coluna com o número da versão antes do nome do pacote original, como abaixo:

    $ 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. Agora devemos classificar a lista para impedir a desinstalação de imagens mais recentes que a que está sendo executada no momento. sort -k1,1 --version-sort -rnos dando o seguinte:

    $ 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. Agora retire os arquivos atuais e mais recentes do kernel, sed -e "1,/$(uname -r | cut -f1,2 -d"-")/d" | grep -v -e `uname -r | cut -f1,2 -d"-"`fornecendo-nos o seguinte:

    $ 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. Agora retire a primeira coluna que adicionamos awk '{print $2}'para obter exatamente o 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. Agora podemos fornecer isso ao gerenciador de pacotes para remover tudo automaticamente e reconfigurar o grub:

    Eu recomendo fazer uma execução a seco primeiro (embora, para seus fins de script, isso possa não ser prático se você tiver um ambiente 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

    Agora, se tudo estiver bem, vá em frente e remova-o com:

    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

Mais uma vez, o objetivo deste "one-liner" é remover apenas os kernels MAIS ANTIGOS do que o kernel atualmente em execução (o que deixa os kernels recém-instalados ainda disponíveis)

Obrigado, deixe-me saber como isso funciona para você e se você pode melhorá-lo!

user313760
fonte
Verifique minha resposta
jarno
1

Você pode simplesmente listar o diretório / boot para ver as versões do kernel que você possui usando o comando 'ls'. Em seguida, use 'sudo apt-get -y purge "xxx"', onde "xxx" é substituído pelo número da versão que você deseja remover. Cuidado para que não seja a versão que você está executando no momento !!.

Natarajan
fonte
1
Eles queriam um comando de uma linha. Você solução precisa de mais de 1 linha
Anwar
1

Instale bikeshed( apt install bikeshed) e chame purge-old-kernelscomo root.

$ sudo purge-old-kernels
Fluxo
fonte
os kernels de limpeza e remoção foram obsoletos, em favor de "apt autoremove". Se você tiver problemas, por favor, registre bugs no apt. Veja bugs.launchpad.net/bikeshed/+bug/1569228/comments/7
Amedee Van Gasse
0

Uma resposta rápida, explicação mediante solicitação:

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
fonte
2
Sugiro (ou, se preferir, solicite) expandir isso para incluir a explicação. :)
Eliah Kagan
@EliahKagan, acho que o roteiro não faz sentido. Por que pular pacotes que não desejam ser instalados? O script pode remover alguns kernels mais recentes que o atual ou salvar outros mais antigos por sed '$d'comando. O script não remove nenhum pacote de cabeçalho linux ou outros pacotes relacionados aos pacotes do kernel a serem removidos, além de não excluir os arquivos de configuração dos pacotes. Eu recomendaria usar minha resposta.
Jarno
@EliahKagan Na verdade, o script não remove nenhum pacote, mas imprime (por echo) o apt-getcomando que você pode executar.
Jarno
0

Eu me cansei muito de toda essa complexidade desnecessária e criei um pacote Python que torna o one-liner trivial:

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

Instale-o com

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

Veja mais em https://github.com/mrts/ubuntu-old-kernel-cleanup .

Espero que isso ajude outras pessoas também.

Mrts
fonte
Tentei isso, mas eu recebo o erro: "ubuntu-old-kernel-cleanup: comando não encontrado"
Grant
Então não está no seu caminho de pesquisa executável. Você o instalou sudo pip install ...como descrito acima? sudoé realmente importante; caso contrário pip, ele será instalado em algum diretório do usuário e o shell poderá não procurar neste arquivo arquivos executáveis.
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 o tempo todo, e até o ubuntu 17.10

David Ramsay
fonte
Para mim, isso tenta remover linux-libc-dev:amd64, o que é indesejável.
Malte Skoruppa