Linux Tweaks, HowTo's and Reviews

A while back I wrote a post on how to remove old kernels from your Ubuntu system. While that process works just fine, it is a four step process. One person who read that post left a comment with a nice command line one-liner that removes all but the currently running kernel. And while that one-liner works quite well, I must admit that I don't understand all the regular expressions used in it, so I decided to try and come up with my own one-liner to remove the old kernels from my system.

I'm going to take you through this step by step so you can see how the individual commands in this one-liner tie together. If you're impatient, you can skip to the end to see the final command.

Step 1) List all packages that start with "linux-"

We'll use the dpkg command with the -l switch to list the packages, whether installed or not, that start with the string linux-.

dpkg -l linux-*

Step 2) Filter that list to show only installed packages

To filter the list, I'm going to pipeline the output of the first command into the awk command. I'm also going to use awk to filter out everything but the package names.

dpkg -l linux-* | awk '/^ii/{ print $2 }'

Step 3) Filter out packages for the currently running kernel

OK, so now I'm down to a pretty limited number of packages, but I don't want to remove the packages for my currently running kernel. I'm going to use a few commands to do that. First off, I can determine my currently running kernel with the uname -r command. Currently on my system that command outputs: 2.6.32-25-generic.

To do my package filtering, I only want the numeric portion of that output. I'll pipeline the output of uname -r and use the cut command with a hyphen as the field delimiter. I'll cut fields 1 & 2.

uname -r | cut -f1,2 -d"-"

Now I'm going to use this result as the filter for a grep command. In Linux, to use the result of one command as an argument in another command, you enclose the command in single back-quotes ( ` that's the key to the left of the 1 on a standard US keyboard). So here's my one-liner so far.

Step 4) Filter the list for only the kernel packages

So now I have a package list that excludes the packages for my current kernel. The only packages from the list above that I want to remove are: linux-headers-2.6.32-24, linux-headers-2.6.32-24-generic, linux-image-2.6.32-24-generic.

What makes these packages unique from the others in the list is that they all contain numbers. So I can use grep again to filter the list down to only packages with numbers in their names. I'll pipeline the output of the previous command into grep -e [0-9].

Step 6) Putting it all together: Removing the packages

So now that I have a good list of packages I can use another pipe and the xargs command to invoke apt-get to remove the packages. First I'm going to show it using the --dry-run switch with apt-get. That way you can give it a try without actually changing your system.

So there you have it. One command, albeit a long one, to remove the old kernels from your Ubuntu system. I imagine the same command should work on other Debian based systems as well, but I've only tested this on my 32 bit system. I'd be interested to know if it works just as well on a 64 bit system.

107 Comments

Hi,
Thanks so much for the best one-liner I ever heard !!
Happened accross your pages 15th Oct 2016 from Ireland
Just used it on my Zorin package and it still worked great after all this time.
Its a pity you have not posted much recently as your solutions are so to the point, very engineer like "get it right first time".

NOT WORKING (LinuxMint Cinnamon 16.04 x64). The script removes the most recent kernel (4.4.0-28) while leaving the old one (4.4.0-21). Not sure what goes wrong, I would prefer to purge the old kernel instead.

I had an issue with my boot partition being 100% full on Ubuntu 14.04 and apt-get failing to work all together. So I ended up changing the final command to use dpkg to remove them.... So the final full command is ...

When i try the dry-run command I recive:
The following packages have unmet dependencies:
linux-image-generic-lts-quantal : Depends: linux-image-3.5.0-54-generic but it is not going to be installed
E: Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution).

Mine kernel is 3.5.0-45, but I don't know what is the package that depend from them...
I can't use apt-get -f install because my boot partition is full..
Can anyone help me please ?
Thank you very much, and apologize for my bad english!

After confirming with "--dry-run remove" that only images and headers would be removed in my 12.04 64 bit system, I pulled the trigger.
The last line of the result is "Do you want to continue [Y/n]? Abort." and I'm returned to the prompt as if I'd aborted the command.
Haven't got a clue what went wrong.

will fix it in the case of libc-dev:amd64. Maybe I should make it so it only includes packages that start with "linux-image" or "linux-headers", just in case some other problem package shows up. I can do that by adding

Cheers for that. Maybe you could modify the date at the top of the page. It currently says October 2nd 2010 which gives a first impression that it's 3 years old. And under the Updates include this latest tweak.
Regards

It is only supposed to match kernel related packages but it is also finding packages like linux-libc-dev:amd64. As it begins with linux and has a number. People could get caught out with this and
remove things they dont want removing.

I also have linux-libc-dev:amd64 installed, which became a candidate for removal because of the last check "only keep items with numbers in them". Of course, including "grep -v libc" in the command solved this.

a nice command line one-liner that removes all but the currently running kernel
While this is an accurate description, it might not be what we want. Imagine the following situation:
1. I do `sudo apt-get update` and see that a new kernel is available.
2. I install the new kernel. I don't reboot (why would I, if everything works fine?)
3. I realize that I have a bunch of older kernels in my system. I want to remove them.
4. I run this one-liner. Instead of deleting everything except the newest kernel, it deletes everything except the currently running one. No gain.

Why do you say no gain? You still got rid of all of those old kernels, didn't you? And removing the running kernel certainly seems like a bad idea. In your scenario, the next time you install updates you should get the new kernel again. Now you only have one extra kernel rather than 5 or 10.

Hmm I think it willl work on a 64 system, im currently in the process of adjusting it a little though. The trick of picking out everything that contain numbers for processing doesnt work on 64 systems since we have filenames like *amd64 and so.

I have used your technique to remove old kernels and it has worked quite well in he past, and I thank you for that. However now, after upgrading from linux-headers-2.6.32-45 (2.6.32-45.103) to 2.6.32-45.104, when I type:
:~$ dpkg -l linux-*
I get this:
No packages found matching linux-3.7.1.
No packages found matching linux-3.8.0.
What gives?

I found the answer to my own question.
It has nothing to do with the upgrade. It has something to do with me taking the crash course on how to build and run a Linux kernel. I built two kernels (3.7.1 and 3.8.0) but never run them. They are sitting in folders with said names in my Home Folder.
I feel kinda dumb...
Thanks

Hi Guys
I get this error when using this
E: Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution).

This may be because it failed to install correctly as the /boot is 100% full can you repost the solution showing where you would put just the one image to remove in order to clean space so i can run it again please
Many thanks

Hi Linerd
That worked a treat thanks. My problem was an incomplete linux header update to 3.2.0-34 due to not enough space and everything i did failed because of this. With the old solution i was able to remove older versions one at a time and create the space to complete the update and remove all of the old ones with your in one command

Beware of what you're doing if using this.
This method is not fool-proof.
On my system it wants to remove linux-libc-dev:amd64 too, which is a bad idea.

Yes, it works on on 32-bits systems as the author already wrote, but I'm not so sure about 64-bits systems. There might be other packages that also meet the requirements and that are removed unexpectedly.

superboer12 - great command, worked fine on my 64 bit Lucid. How would I modify this script to keep the last two kernels? I had .39, .40 and .41. After running this I had only .41. Booted OK but I always worry.

Very nice, but this article seems to assume that you're already running the OS with the latest kernel, so as this article goes, it will delete all kernels except the one currently running, which may be an old one.

What would be a better command is if it deleted all kernels except the latest one (and perhaps also the one currently running).

Speaking of which, why doesn't ubuntu default to running the latest one? Despite installing kernel updates, mine's still defaulting to an old one...

Does anyone know how (or whether it makes sense) to delay the execution of the postrm.d scripts until after all the pieces are removed? It gets a little tiresome watching all those initramfs and zz-update-grub calls...

Thanks for the tip. I didn't know those rc status config files were still hanging around. However, when I try your command I get this type of error:

Package linux-image-2.6.32-27-generic is not installed, so not removed

When I modify your command to:

dpkg -l 'linux-*' | awk '/^rc/{ print $2}' | sudo xargs dpkg --purge

it works.

After doing some research, it looks like if I change my one-liner to purge instead of remove, then it should get rid of the config files as well and make this all moot, so I'm updating my post. I'll install an older kernel and re-run the command to see if it works.