4

I have a file that contains a list of OS packages with their release versions. I only need the package names and don't need the release versions.

How can I achieve this using Linux commands or any scripts?

Example:

bind-export-libs-9.11.4-26.P2.el7_9.13
bind-export-libs-9.11.4-26.P2.el7_9.7
bind-libs-9.11.4-16.P2.el7
bind-libs-9.11.4-16.P2.el7_8.6
atk-2.28.1-1.el7
atk-2.28.1-2.el7
at-spi2-atk-2.26.2-1.el7
at-spi2-core-2.28.0-1.el7

In the above list, there are duplicate package entries based on the release version.

How can I extract only the package names?

1
  • With GNU awk: awk 'BEGIN{FS=OFS="-"} {NF-=2; print}' file
    – Cyrus
    Jun 11 at 0:33

5 Answers 5

6

I'm almost sure your list was generated by rpm -qa or something similar, so I'm giving you a command which shows just package names without versions. That could save a lot of work later on:

rpm -qa --queryformat '%{NAME}\n' | sort
0
5

Just trim off everything starting with the second-to-last dash.

Using sed:

sed -r 's/-[^-]+-[^-]+$//' file.txt

Basically, the general format of RPM NVRs is <name_possibly_containing_embedded_dashes_or_digits>-<version_never_containing_dashes>-<release_never_containing_dashes>. Dots or digits don't have any special meaning, so searching for them is never going to work properly.

Running this on the output of rpm -qa should produce the exact same list as rpm -qa --queryformat '%{NAME}\n', as suggested in Artem's answer. Note that there will still be duplicate entries if multiple versions or releases of a single package are installed (the former is the norm for kernel packages or gpg-pubkeys, the latter is typical for i686/x86_64 libraries). Run the result through sort -u if you really only want a list of unique names.

# these two should produce the same output:
rpm -qa | sed -r 's/-[^-]+-[^-]+$//'
rpm -qa --queryformat '%{NAME}\n'
3
  • nice, you can verify whether yours works rpm -qa --queryformat '%{name} %{nvra}\n' | sed -r 's/-[^-]+-[^-]+$//' | awk '$1!=$2{print $1,$2}' Jun 10 at 20:34
  • Note that -r is superseded by the more portable and soon standard -E. If you replace those +s with *s, you don't need -E. \{1,\} is the BRE equivalent of ERE +. Jun 11 at 6:21
  • @StéphaneChazelas Thanks for pointing out -E, I didn't know that. But the pluses feel like the most readable option to me,and we hopefully don't need to worry about ERE not being available (the question is about RPM,which probably makes it non-embedded-Linux only).
    – TooTea
    Jun 11 at 7:21
5

I think TooTea's answer points the right way, not this, which I will still keep up for reference reasons.


Long story short: RPM file name convention is complicated, and as shown below, not unique. There are hence cases where you literally cannot know what the correct package name and versioning is. Still, you can try.

However, since this is complicated, this can't simply be done with sed and regular expressions. Wrong tool for the job. Instead, use Redhat's / Fedora's own tooling. This Python script should give you what you want:

#!/usr/bin/env python3
from dnf.subject import Subject
from sys import stdin
from typing import Set
import hawkey


def filename_to_name_candidates(filename: str) -> Set[str]:
    subj = Subject(filename)
    return {
        candidate.name
        for candidate in subj.get_nevra_possibilities(forms=hawkey.FORM_NEVRA)
    }


names = set()

for line in stdin:
    try:
        names = names | filename_to_name_candidates(line)
    except:
        pass

for name in sorted(names):
    print(name)

Save that somewhere (say, as filename2packagename), make it executable, (chmod 755 filename2packagename). You can then just pipe in your file of filenames:

./filename2packagename < listofnames.txt
3
  • Care to give an example NEVRA that can't be processed with sed? I've never run into one, but that's obviously no proof that they don't exist.
    – TooTea
    Jun 10 at 19:59
  • the problem is not the regular being incapable of parsing, it's that no unique parsing exists, and that picking things apart into the candidates is not simple (note how I put simply emphasized in that answer!) with regex tooling. For how typical regex failures, see for example my comment here where I checked for a a few simple examples where parsing rpm -q output didn't yield the name that rpm said it should. @TooTea Jun 10 at 20:11
  • 2
    That particular parsing is not really just simple, it's wrong. But the method in my answer has always worked for me, so I would be glad to hear any counterexamples.
    – TooTea
    Jun 10 at 20:15
1

Edit: You might want to consider TooTea's answer.

Run this, replacing file.txt with the name of your file.

sed 's/-[[:digit:]].*//' file.txt | uniq

Output with your example:

bind-export-libs
bind-libs
atk
at-spi2-atk
at-spi2-core

Explanation:

  • The sed commands remove everything starting from the first dash followed by a digit.
  • The uniq command then removed duplicate packages.

Note: This will work so long as there's no package whose name contains a dash followed by a digit as part of its name. And so long as every version begins with a digit. If this is not the case, you should update the script accordingly.

7
  • Don't think this is reliable! See this Oracle guide: this package has the name oracle-database-ee-19c, but its version is 1.0, in oracle-database-ee-19c-1.0-1.x86_64 Jun 10 at 11:17
  • 1
    @MarcusMüller The question is impossible to answer because there are corner cases sed will never be able to work around. Jun 10 at 11:19
  • @ArtemS.Tashkinov Exactly the point of my answer :) Jun 10 at 11:20
  • 2
    @MarcusMüller I am rarely dissuaded of my own cynicism, so I won't try the same on others. The one thing I can say is that my answer is what I would give face-to-face to a dear friend who comes to me with this problem. I appreciate your counterexample, your count, and your observation that this is likely a list of RPM packages - which I ignored. Jun 10 at 11:43
  • 2
    The general format of RPM NVRs is <name_possibly_containing_embedded_dashes_or_digits>-<version_never_containing_dashes>-<release_never_containing_dashes>. So you have to strip off everything starting with the second-to-last dash. Dots or digits don't have any special meaning, so searching for them is never going to work properly.
    – TooTea
    Jun 10 at 19:40
0

Using sed

$ sed -En 's/([^.]*)-.*/\1/;G;/^(.*)\n.*\n\1/d;H;P' input_file
bind-export-libs
bind-libs
atk
at-spi2-atk
at-spi2-core
2
  • 2
    fails for real-world package names like ntfs-3g, ntfs-3g-libs jsr-305, polkit-qt5-1, texlive-12many, texlive-pst-3d, kmod-nvidia-470xx, libusb-compat-0.1, xorg-x11-fonts-ISO8859-1-100dpi and a few others Jun 10 at 11:35
  • The general format is <name_possibly_containing_embedded_dashes_and_digits>-<version_never_containing_dashes>-<release_never_containing_dashes>. So you have to strip off everything starting with the second-to-last dash. Dots or digits don't have any special meaning.
    – TooTea
    Jun 10 at 20:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .