10

What does the following code do?

I found it on internet so I copied it and pasted it into terminal but when I installed a new kernel it only detects the old one.

sudo apt-get remove --purge $(dpkg -l 'linux-image-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d')
  • The second part is a regular expression, defining how the file name should look but not exactly which file name it is. Is it installing a kernel that is your objective or is it finding out exactly what the command removes? – Niklas Lindskog Apr 16 '15 at 18:57
  • This code will not install any kernel, but remove them! I don't see what exactly this sed-command does, but it processes the output of dpkg -l 'linux-image-*', which lists all installed kernel packages and filters some of them out for deletion by apt-get remove. – Byte Commander Apr 16 '15 at 19:04
  • 16
    A very good rule of thumb to live by: if you don't know what a code does, don't put that code into Terminal until someone who knows what it does tells you. If you're trying to get the most up to date kernel, you may want to look at askubuntu.com/questions/119080/… instead of using a code you don't understand. – Colonel Trogdor Apr 16 '15 at 19:45
  • Old kernels sitting around on disk cause no trouble, except for taking up disk space, and making update-grub (package postinst) slower. If you're worried that you didn't actually boot the kernel you were trying to upgrade to, then run uname -a, or even dmesg | less – Peter Cordes Apr 16 '15 at 23:32
  • 7
    "I found it on internet so I copied it and pasted it into terminal" NO NO NO NO NO. You'll end up doing horrible things by accident. Running code you don't understand results in terrible things like nuking your system, and puppies being kicked. – Journeyman Geek Apr 17 '15 at 0:36
12

Let's break the code one by one (for my system):

$ dpkg -l 'linux-image-*'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                          Version             Architecture        Description
+++-=============================-===================-===================-================================================================
un  linux-image-3.0               <none>              <none>              (no description available)
ii  linux-image-3.13.0-32-generic 3.13.0-32.57        amd64               Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-extra-3.13.0-32-g 3.13.0-32.57        amd64               Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-generic           3.13.0.32.38        amd64               Generic Linux kernel image


$ dpkg -l 'linux-image-*' | sed '/^ii/!d'
ii  linux-image-3.13.0-32-generic                         3.13.0-32.57                                        amd64        Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-extra-3.13.0-32-generic                   3.13.0-32.57                                        amd64        Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-generic                                   3.13.0.32.38                                        amd64        Generic Linux kernel image


$ uname -r | sed 's/\(.*\)-\([^0-9]\+\)/\1/'
3.13.0-32



$ dpkg -l 'linux-image-*' | sed '/^ii/!d' | sed "/$(uname -r | sed 's/\(.*\)-\([^0-9]\+\)/\1/')/d"
ii  linux-image-generic                                   3.13.0.32.38                                        amd64        Generic Linux kernel image



$ dpkg -l 'linux-image-*' | sed '/^ii/!d' | sed "/$(uname -r | sed 's/\(.*\)-\([^0-9]\+\)/\1/')/d" | sed 's/^[^ ]* [^ ]* \([^ ]*\).*/\1/'
linux-image-generic



$ dpkg -l 'linux-image-*' | sed '/^ii/!d' | sed "/$(uname -r | sed 's/\(.*\)-\([^0-9]\+\)/\1/')/d" | sed 's/^[^ ]* [^ ]* \([^ ]*\).*/\1/' | sed '/[0-9]/!d'
## No output

So this would do nothing:

$ sudo apt-get remove --purge $(dpkg -l 'linux-image-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d')

As you can see it was very close to removing my current kernel meta package linux-image-generic 3.13.0.32.38 amd64 Generic Linux kernel image (thanks to this last sed '/[0-9]/!d' line) , which is wrong.

In a nutshell, this piece of sed has several issues. You should avoid it to remove your older kernels.

EDIT:

I have already said that the sed command has issues. For the sake of extension i am going to remove the last two lines with the form that actually should be working in my current scenario.

The last two lines in a modified form:

sed -r 's/^[^ ]* *(([a-z]|-)*)(-[a-z]*) *(([0-9]|-)*)\..*/\1-\4\3/'

So:

$ dpkg -l 'linux-image-*' | sed '/^ii/!d;/'"$(uname -r | 
sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d' | 
sed -r 's/^[^ ]* *(([a-z]|-)*(-[a-z]*) *(([0-9]|\.|-)*)\..*/\1\-\4\3/'

Outpus:

linux-image-3.13.0.32-generic

Thats very dengerous!!

Now showing what this would do to my system, --dry-run ning the full apt-get remove command:

$ sudo apt-get remove --dry-run $(dpkg -l 'linux-image-*' | 
sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d' | 
sed -r 's/^[^ ]* *(([a-z]|-)*)(-[a-z]*) *(([0-9]|\.|-)*)\..*/\1\-\4\3/')


Reading package lists... Done
Building dependency tree       
Reading state information... Done
Note, selecting 'linux-image-3.13.0-32-generic' for regex 'linux-image-3.13.0.32-generic'
The following package was automatically installed and is no longer required:
  linux-headers-generic
Use 'apt-get autoremove' to remove it.
The following packages will be REMOVED:
  linux-generic linux-image-3.13.0-32-generic
  linux-image-extra-3.13.0-32-generic linux-image-generic
0 upgraded, 0 newly installed, 4 to remove and 0 not upgraded.
Remv linux-generic [3.13.0.32.38]
Remv linux-image-generic [3.13.0.32.38]
Remv linux-image-extra-3.13.0-32-generic [3.13.0-32.57]
Remv linux-image-3.13.0-32-generic [3.13.0-32.57]

As you see it will remove all my current kernels, also note that this is just a simulation to match my current scenario and the original sed can be improved.

** Note that the actual code will work if do a reboot after kernel upgrade as Oli already mentioned.

EDIT2:

Here is a simple yet robust script that will consider all situations and remove the actual older kernels:

#!/bin/bash
kernels=( $(grep -Po "^linux-image-[^-]+-[^-]+-generic\b" < <(dpkg --get-selections)) )
cur_rel=$(grep -Po ".*(?=-[a-z]*$)" < <(uname -r))

for kernel in "${kernels[@]}"; do
    ker_rel=$(grep -Po "[0-9].*(?=-[a-z]*)" <<< "$kernel")
    dpkg --compare-versions "$ker_rel" gt "$cur_rel" && { echo "Please Restart your computer first"; break; }
    dpkg --compare-versions "$ker_rel" lt "$cur_rel" && sudo apt-get remove "$kernel"
done

If you have any version that is newer than the current one this will give you a warning to restart you computer first. Also note that the older kernels are preserved due to a good reason which is if you somehow mess up your current kernel making your system unstable then you should be able to boot into any older kernel.

  • Um, wait, what? You replace the original, working code with some other code that you think "actually should (!!) be there", observe that your "fixed" code is broken and would remove your current kernel, and thus conclude that the original code is "very dengerous!!"? WTF? -1. – Ilmari Karonen Apr 16 '15 at 23:47
  • There are certainly valid reasons to criticize the original code, as pointed out e.g. in Oli's answer, and I'm sure the sed code could be made more robust. But that fact remains that, under normal circumstances (e.g. you've rebooted after your last kernel upgrade), the original code works, whereas your modified version doesn't. – Ilmari Karonen Apr 16 '15 at 23:55
  • 1
    First of all you should not use the last word you have used..now, did you actually run the actual code? removing the apt-get part and what output did you get? – heemayl Apr 16 '15 at 23:58
  • @IlmariKaronen: well, i second with you in some points, i have modified the answer.. – heemayl Apr 17 '15 at 0:21
  • Why, yes, I did run your modified command (the version that doesn't crash due to a missing parenthesis after (([a-z]|-)*, that is) without the apt-get part before criticizing it. On my system, it outputs the following lines: linux-image-3.13-, linux-image-3.13-, linux-image-extra-3.13-, linux-image-extra-3.13-, linux-image-3.13.0.49-generic. Is that really what you intended? (Also, bonus points if you can figure out which kernels I actually have installed.) – Ilmari Karonen Apr 17 '15 at 14:10
12

This is an effort to remove kernels that aren't the current one.

There are many bits of code out there that do something like this and this is it not a good example. It could easily remove newer-than-current kernels and therefore the meta-package that installs (linux-generic et al) if you had an upgrade and didn't reboot.

  • 2
    And this is not a good idea at all. Those older kernels are kept for reason, by default there are only 2 old and a current one. – Barafu Albino Apr 16 '15 at 20:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.