14

I'm curious what commands a certain package provides my system with. By command, I mean an in-path executable that I can run from the command line (ls, grep, sed, etc).

I'm not trying to work out the package from the command, which can be done with:

dpkg -S `which command`

I want the opposite, a list of commands from a package.

2

4 Answers 4

8

The following little loop will handle this with installed packages.

$ for f in $(dpkg -L login); do [[ $(type -P "${f##*/}") == "$f" ]] && echo ${f##*/}; done
nologin
lastlog
newgrp
faillog
su
login
sg

How it works:

  • dpkg -L package generates a list of all the files in a package which we iterate through.
  • We strip off the directory name with a little bashism: ${f##*/} and
  • Using the bash-builtin type -P command we see if that command is both in the path and that its path equals the file we started with.
  • We finish by pumping out the shortened command.
  • [[ condition ]] && command is just a bash shorthand for an if..then statement.

It's important to note that not all packages contain the commands you would expect them to. Apache is split out over multiple packages (with -common and -bin subpackages) and the vlc command isn't in the vlc package, it's in vlc-nox. There are many examples like that.


This can be adapted per Gilles's idea, doing a string match instead of actually checking, but keeping it all in one bash process (and still using the whole path).

for f in $(dpkg -L login); do [[ $f =~ ^${PATH//:/|} ]] && echo ${f##*/}; done

The major difference here is the [[$f =~ ^${PATH//:/|} ]]. That's an in-Bash regex search. The ${PATH//:/|} part is taking the contents of $PATH and is hacking them into a dirty little regex. The condition should check the string begins with part of the path.

3
  • I tried for f in $(dpkg -L apache2); do [[ $(type -P "${f##*/}") == "$f" ]] && echo ${f##*/}; done NO results
    – user259474
    Mar 19, 2014 at 10:42
  • 1
    @user259474 This is expected. The apache2 package doesn't contain much, it's more a of a meta package. The commands are in -common and -bin subpackages.
    – Oli
    Mar 19, 2014 at 10:45
  • 1
    That's a bizarre way of doing it: more complex and less reliable than looking for /bin and friends textually. Mar 19, 2014 at 12:00
4

List the files in the package which are in a directory in the PATH. You only have to consider the default PATH, not any user customizations, since packages only use standard directories.

dpkg -L PACKAGE-NAME… | sed -n 's!^\(/s\?bin\|/usr/s\?bin\|/usr/games\)/!!p' | sort

Remove the s\? parts if you only want the programs intended for ordinary users without sudo.

If the package isn't installed, replace dpkg -L by apt-file -F list.

This misses a few programs because they are provided via alternatives. For example, for the ftp package, only netkit-ftp and pftp are provided, but this package actually provides the ftp command, because /usr/bin/ftp is a symbolic link to /etc/alternatives/ftp which is a symbolic link to one of the ftp implementations on the system, potentially /usr/bin/netkit-ftp. The following command (which isn't an example of good programming, just a big one-liner) lists the commands provided by a package via the alternatives mechanism, as currently configured.

perl -lwe 'foreach (`dpkg -L @ARGV`) {chomp; ++$p{$_}} foreach (</bin/* /sbin/* /usr/bin/* /usr/sbin/*>) {$e = readlink; next unless defined $e and $e =~ m!^/etc/alternatives/!; $t = readlink $e; print if $p{$t}}' PACKAGE_NAME…

If you want to list the commands that could be provided via an alternative which is currently configured to point to a different package, you need to parse the files in /var/lib/dpkg/alternatives.

Symbolic links and configuration files that implement the alternatives mechanisms are not registered in packages but registered automatically in postinst, which makes it difficult (and in fact technically impossible if a package's installation script doesn't follow conventions) to query the alternatives provided by an uninstalled package.

6
  • I'm a bit wary of apt-file mostly because it entails downloading a large amount of data before you can start using it. For a lot of packages, some sort of spin around apt-get download would be faster.
    – Oli
    Mar 19, 2014 at 12:58
  • 1
    @Oli I wish that apt-file was part of the default installation. The amount of data is only a little more than one non-incremental round of apt-get update, and it's something you need to do once per release. Mar 19, 2014 at 13:09
  • lightdm appears to the only path in $PATH that buck's the trend here. You're not catching /usr/lib/lightdm/ which contains executables for some ungodly reason. Approach this from the other side... Apart from perhaps taking a little longer, is checking the active path going to be erroneous?
    – Oli
    Mar 19, 2014 at 13:24
  • Just noticed you rolled this back. Without -F, apt-file list is very baggy (and doesn't answer the question at all). Try it with login if you want to see what I'm talking about.
    – Oli
    Mar 19, 2014 at 16:25
  • 1
    dlocate works wonderfully for installed packages, even gets the ones in /lib though it doesn't deal with alternatives.
    – terdon
    Mar 20, 2014 at 18:19
1

My other answer is fairly certain but only works for installed packages. Here's a crack at a version that works for packages not yet installed (but ones that are only available in the main repositories)

export PACKAGE="login"; source /etc/lsb-release; source <(dpkg-architecture); for f in $(wget -qO- "http://packages.ubuntu.com/$DISTRIB_CODENAME/$DEB_BUILD_ARCH/$PACKAGE/filelist" | sed -n '1,/<pre>/d;/<\/pre>/,$d;p'); do [[ $f =~ ^${PATH//:/|} ]] && echo ${f##*/}; done

It's horrifically more complicated so I'll write a broken down version:

export PACKAGE="login";
source /etc/lsb-release;
source <(dpkg-architecture);

URL="http://packages.ubuntu.com/$DISTRIB_CODENAME/$DEB_BUILD_ARCH/$PACKAGE/filelist"

# We grab the packages.ubuntu.com version of the file list (and strip it with sed)
for f in $(wget -qO- "$URL" | sed -n '1,/<pre>/d;/<\/pre>/,$d;p'); do

    # We then compare every file provided in the package with every path stub
    [[ $f =~ ^${PATH//:/|} ]] && echo ${f##*/};
done
1
  • Unless you have very limited bandwidth and are installing from CD, it's simpler to use apt-file. And again use hard-coded directories, not the user's $PATH. Mar 19, 2014 at 13:11
0

An easier method is just querying the web:

release=$(lsb_release -sc)
arch=$(uname -m)
package=$@

for i in $package; do
   printf "List of files in $i package"
   curl http://packages.ubuntu.com/$release/$arch/$i/filelist 2>/dev/null | grep -oP '/[\w\d/.]+$
done

This has the advantage that you would only need curl and sed (what Ubuntu system doesn't have them) and you can change it with wget easily if necessary. It queries several packages with a single command which is a plus.


Any equivalent with other programing language + html parser should work better and less likely to break if something change in the package list.

Changing the host you can also query Debian database.

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