Find out which package installed a certain command

Although the option is easily overlooked (because the description is a bit vague) it is a build-in in 'dpkg'

Use

dpkg -S  commandname

to find out from which package this command was installed.

example:

user@computer:~$ dpkg -S $( which cupsd )
cups-daemon: /usr/sbin/cupsd

user@computer:~$ dpkg -S alsamixer
alsa-utils: /usr/bin/alsamixer
alsa-utils: /usr/share/man/man1/alsamixer.1.gz

This is very useful if you want to reinstall/remove/reset certain commands/apps from your system in a clean way

6 Likes

Another excellent gem!

Do you know why it happens that "which" returns no path, but "whereis" will, with extras ?

Is there a way to "convert" all the "whereis" references so that "which" will report those when asked?

Example for me:

root:~# dpkg -S $( which gvim )
dpkg-query: no path found matching pattern /usr/bin/gvim

root:~# whereis gvim
gvim: /usr/bin/gvim /usr/share/man/man1/gvim.1.gz

1 Like

I have vim on my um24. Here's the chat:

user@um24:~$ which vi
/usr/bin/vi
user@um24:~$ which vim
/usr/bin/vim
user@um24:~$ whereis vim
vim: /usr/bin/vim /etc/vim /usr/share/vim /usr/share/man/man1/vim.1.gz
user@um24:~$ whereis vi
vi: /usr/bin/vi /usr/share/man/man1/vi.1.gz
user@um24:~$

Am I missing something?

1 Like

But why?

:~$ which bash
/usr/bin/bash
:~$
1 Like

Pro memori:

  1. which locates a command
  2. whereis locates the binary, source, and manual page files for a
    command

which gvim returns /usr/bin/gvim on my system.

dpkg -S /usr/bin/gvim returns indeed an error because gvim does not exist as such in the package vim-gtk3

How come ?, let's see what gives:

user@computer:~$ which gvim
/usr/bin/gvim

user@computer:~$ dpkg -S $( which gvim )
dpkg-query: geen pad gevonden dat overeenkomt met /usr/bin/gvim

user@computer:~$  file /usr/bin/gvim
/usr/bin/gvim: symbolic link to /etc/alternatives/gvim

user@computer:~$ file /etc/alternatives/gvim
/etc/alternatives/gvim: symbolic link to /usr/bin/vim.gtk3

user@computer:~$ dpkg -S /usr/bin/vim.gtk3
vim-gtk3: /usr/bin/vim.gtk3

It turns out that /usr/bin/gvim is not part of the vim-gtk3 package.
It is a softlink to /usr/bin/vim.gtk3 that is created during post-install.

So, this should be the solution to avoid the given situation:

user@computer:~$ dpkg -S $( realpath $( which gvim ) )
vim-gtk3: /usr/bin/vim.gtk3
4 Likes

Again, I guess I didn't explain myself well. Sorry!

I've used both which and whereis since 1984, my first introduction to HP-UX.

What I don't understand, to this day, is why, sometimes "which" reports nothing for the command path, but "whereis" will report that desired command path (along with other stuff, including multiple instances if such apply)!

... and Thom did answer that question. :slight_smile:

If I want to locate all gvim references in my PATH, I can use the following little hack:

#!/bin/bash

command="gvim"

echo "${PATH}" |
	awk '{
		n=split( $0, path, ":" ) ;
		for( i=1 ; i <=n ; i++ ){
			print path[i] ;
		} ;
	}' |
while read line
do
	this="${line}"/${command}

	if [ -e "${this}" ]
	then
		ls -ld "${this}"
	fi
done

For me, that reports:

lrwxrwxrwx 1 root root 22 Apr  9  2021 /usr/bin/gvim -> /etc/alternatives/gvim
lrwxrwxrwx 1 root root 22 Apr  9  2021 /bin/gvim -> /etc/alternatives/gvim

If we take a look at /etc/alternatives/, we see multiple references pointing to that same vim.gtk3 binary:

ls -l /etc/alternatives/*vi*
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/eview -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/evim -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/gview -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/gvim -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/gvimdiff -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/rgview -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/rgvim -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/rview -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/rvim -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/vi -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/view -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/vim -> /usr/bin/vim.gtk3
lrwxrwxrwx 1 root root 17 Apr  9  2021 /etc/alternatives/vimdiff -> /usr/bin/vim.gtk3

The issue I have with your proposed solution is that "vim.gtk3" is coded to verify which "personality" to adopt at runtime. That is determined by the command string used (i.e. gvim, vs eview vs vi) when called.

Yes, it has a default, but

  • I have no clue if that default is what I use, and
  • I don't want to bother with trying to figure that out.

Since the symlinks are created as part of the installation process, my expectation (as a user) is that those references should be reported by "which".

Oh but they do:

user@computer:~$ which rgview
/usr/bin/rgview

It's just that dpkg -S can't retrace them to a package.

2 Likes

And yet another alternative:

https://www.debian.org/distrib/packages

image

3 Likes

Thank you, Eugene, but that does not report a unique package for a specified command.

For example, "dd" gives the following:

DebianSearch__dd

I am looking for for a one-to-one correspondence of command -> package.

I am trying to build a tool that will report to me all the packages that must be installed for all the tools that have been used in my various scripts.

I am getting there, but there is a this issue of uniqueness of identifiable package for a specified command which is problematic.

Not only that,

which dd

reports

/usr/bin/dd

"whereis" reports that same fullpath for dd. But as was shown in the image above, it is shown as being "/bin/dd" within the "coreutils" package !!!

So ... why is "which" giving the "/usr/bin/dd" path ???

The solution offered by Thom (@tkn) comes back with too many instances not locating the associated package.

When I try

apt-file search /usr/bin/dd

the results are categorically useless:

cod-tools: /usr/bin/ddl1-to-ddlm          
cod-tools: /usr/bin/ddlm_validate
cyclonedds-tools: /usr/bin/ddsconf
cyclonedds-tools: /usr/bin/ddsperf
ddate: /usr/bin/ddate
ddccontrol: /usr/bin/ddccontrol
ddcutil: /usr/bin/ddcutil
ddd: /usr/bin/ddd
dde-calendar: /usr/bin/dde-calendar
ddgr: /usr/bin/ddgr
ddir: /usr/bin/ddir
ddpt: /usr/bin/ddpt
ddpt: /usr/bin/ddptctl
ddrescueview: /usr/bin/ddrescueview
ddrutility: /usr/bin/ddru_diskutility
ddrutility: /usr/bin/ddru_findbad
ddrutility: /usr/bin/ddru_ntfsbitmap
ddrutility: /usr/bin/ddru_ntfsfindbad
ddrutility: /usr/bin/ddrutility
dds2tar: /usr/bin/dds-dd
dds2tar: /usr/bin/dds2index
dds2tar: /usr/bin/dds2tar
dds2tar: /usr/bin/ddstool
ddtc: /usr/bin/ddtc
ddupdate: /usr/bin/ddupdate
ddupdate: /usr/bin/ddupdate-config
devscripts: /usr/bin/dd-list
djvulibre-bin: /usr/bin/ddjvu
gddrescue: /usr/bin/ddrescue
gddrescue: /usr/bin/ddrescuelog
gworkspace.app: /usr/bin/ddbd
i2c-tools: /usr/bin/ddcmon
ncbi-tools-x11: /usr/bin/ddv
printer-driver-foo2zjs: /usr/bin/ddstdecode

That output doesn't even include the "proper" reference to "dd", what the man page states refers back to IBM's "data definition", but what I've always referred to ad "data dump". :frowning:

When I try

apt-file --regexp search '/dd$'

it reports almost the same as the web search offered by Eugene:

9base: /usr/lib/plan9/bin/dd              
bash-completion: /usr/share/bash-completion/completions/dd
coreutils: /bin/dd
elpa-yasnippet-snippets: /usr/share/yasnippet-snippets/go-mode/dd
elpa-yasnippet-snippets: /usr/share/yasnippet-snippets/html-mode/dd
klibc-utils: /usr/lib/klibc/bin/dd

but ... we're still not at a one-to-one correspondence.

I even came across this possible mechanism,

dlocate -S /usr/bin/dd

which gave me these multiple (irrelevant) references:

gddrescue: /usr/bin/ddrescue
gddrescue: /usr/bin/ddrescuelog
printer-driver-foo2zjs: /usr/bin/ddstdecode
djvulibre-bin: /usr/bin/ddjvu

One ray of sunshine comes with this approach,

/usr/lib/command-not-found dd

which reports

Command 'dd' is available in the following places
 * /bin/dd
 * /usr/bin/dd
dd: command not found

which is promising, since it lists the only 2 relevant instances, and, using

/usr/lib/command-not-found --ignore-installed dd

gives us

Command 'dd' not found, but can be installed with:
apt install coreutils

I could work with that, doing some output parsing, but I would have to work thru each individual case to confirm whether in fact "all bases were properly covered !"

Even if I was desperate enough to scan thru all the man pages for references like this one,

Full documentation https://www.gnu.org/software/coreutils/dd

not all man pages actually provide such a unique and precise reference to their originating package.

Once I do have all the package labels, I can then use

apt search -n ^${packageName}$

to get the report of the packages applicable to current usage (in case of incompatibilities with later versions).

So, in the hopes of taking this discussion to another level, I will include here the code for my script so far (admittedly a hack and evidently not elegant).

Script "OS_Admin__IdentifyRequiredPackagesForCoding.sh":

#!/bin/bash

####################################################################################################
###
###	WORK-IN-PROGRESS
###
###	Script to scan local directory containing custom scripts and
###	create a report of package names that are required to be installed
###	in order to use all "system" or "3rd-party" tools referenced in those scripts.
###
####################################################################################################
#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+


locateAllCustomTools()
{
cd ${Oasis}/bin
find . -xdev -executable \( ! -type d \) \( ! -type l \) \( ! -name '*,v' \) | cut -c3- | sort -r >"${tmp}"

while read file
do
	case "${file}" in
		*.sh | *.bash | *.bh )
			echo "SHELL|${file}"
			;;
		* )
			testor=$( head -1 "${file}" |
				grep '^#!' |
				sed 's+[#][!][ ]/+#!/+' |
				awk '{
					n=split( $0, headstr, " -") ;
					print headstr[1] ;
				}' )
			if [ -n "${testor}" ]
			then
				case "${testor}" in
					"#!/bin/sh" | \
					"#!/bin/bash" | \
					"#!/bin/ksh" | \
					"#!/bin/csh" | \
					"#!/usr/bin/env bash" )
						echo "SHELL|${file}"
						;;
					"#!/usr/bin/perl" )
						echo "SCRIPT|${file}"
						;;
					* )
						echo "NON_SHELL|${file}"
						;;
				esac
			else
				echo "IGNORED|${file}" >&2
			fi
			;;
	esac
done < "${tmp}" 2>>/dev/null | sort -r >${tmp}.files
} # locateAllCustomTools()

locateAllCustomTools
#more ${tmp}.files
echo " Done 'locateAllCustomTools' ..."

#dpkg -S $( realpath $( which gvim ) )
harvestUtilitiesUsed()
{
	rm -f "${start}/${base}_codefunction"

	grep -v '^IGNORED' "${tmp}.files" |   #head -5 |
	cut -f2- -d\| |
	while read prog
	do
		rm -f "${tmp}.single"
		awk '{ if( length($1) >1 ){ print $0 ; } ; }' <"${prog}" |
		awk '{ if( \
			$1 !~ /^#/ && \
			$1 !~ /^[$]/ && \
			$1 !~ /^[[:digit:]]/ && \
			$1 !~ /^[[:punct:]]/ && \
			$1 !~ /^[[:alpha:]]*[:]/ && \
			$1 !~ /[+]/ && \
			$1 !~ /[-]$/ && \
			$1 !~ /[_]$/ && \
			$1 !~ /[!"#$%&()*,/:;<=>?@[\]^`{|}~]/ && \
			$1 !~ /^-/ ){
				switch( $1 ){
					case /ls/ :
					case /cp/ :
					case /du/ :
					case /df/ :
					case /ln/ :
					case /sh/ :
					case /mv/ :
					case /rm/ :
					case /ps/ :
					case /tr/ :
					case /cut/ :
					case /cat/ :
					case /tac/ :
					case /sed/ :
					case /date/ :
					case /head/ :
					case /tail/ :
					case /head/ :
					case /sort/ :
					case /sync/ :
					case /time/ :
					case /clear/ :
					case /chown/ :
					case /chmod/ :
					case /chgrp/ :
					case /sleep/ :
					case /mount/ :
					case /mkdir/ :
					case /rmdir/ :
					case /touch/ :
					case /umount/ :
						break ;
					case /if/ :
					case /fi/ :
					case /do/ :
					case /;;/ :
					case /for/ :
					case /done/ :
					case /case/ :
					case /esac/ :
					case /then/ :
					case /elif/ :
					case /else/ :
					case /while/ :
					case /until/ :
					case /return/ :
					case /select/ :
					case /coproc/ :
					case /function/ :
						break ;
					case /bg/ :
					case /cd/ :
					case /fc/ :
					case /fg/ :
					case /let/ :
					case /pwd/ :
					case /set/ :
					case /bind/ :
					case /dirs/ :
					case /echo/ :
					case /eval/ :
					case /exec/ :
					case /exit/ :
					case /wait/ :
					case /hash/ :
					case /help/ :
					case /jobs/ :
					case /kill/ :
					case /read/ :
					case /test/ :
					case /trap/ :
					case /type/ :
					case /^[;]/ :
					case /^[.]/ :
					case /popd/ :
					case /alias/ :
					case /break/ :
					case /local/ :
					case /shift/ :
					case /shopt/ :
					case /times/ :
					case /umask/ :
					case /unset/ :
					case /pushd/ :
					case /source/ :
					case /caller/ :
					case /disown/ :
					case /export/ :
					case /enable/ :
					case /logout/ :
					case /printf/ :
					case /ulimit/ :
					case /suspend/ :
					case /builtin/ :
					case /getopts/ :
					case /history/ :
					case /mapfile/ :
					case /unalias/ :
					case /declare/ :
					case /typeset/ :
					case /command/ :
					case /compgen/ :
					case /compopt/ :
					case /readonly/ :
					case /continue/ :
					case /complete/ :
					case /readarray/ :
						break ;
					case /gsub/ :
					case /substr/ :
					case /split/ :
					case /switch/ :
					case /gensub/ :
						break ;
				###	FUTURES
				#	case /awk/ :
				#	case /sed/ :
				#	case /perl/ :
				#	case /prolog/ :
				#	case /python/ :
				#		break ;
					default:
						if( $2 ~ /^[=]/ || $2 ~ /^[!][=]/ ){
							if( dbg == 1 ){ printf("VAL_ASSIGN: %s\n", $0 ) | "cat 1>&2" ; } ;
						}else{
							print $1 ;
						} ;
						break ;
				} ;
			} ;
		}' | sort | uniq > "${tmp}.single"


		###
		###	Logic to capture per file items and exclude if they match functions in the same file
		###
		while read line
		do
			testor=$( grep "${line}()" "${prog}" )
			if [ -n "${testor}" ]
			then
				echo "FUNCTION: [${prog}] '${line}'" >&2
			else
				echo "${line}"
			fi
		done < "${tmp}.single" 2>>"${start}/${base}_codefunction"


		###
		###	FUTURES - Add logic to look for "dot" imports and scan those to exlude function references
		###
	done | sort | uniq > "${tmp}.utils"
} # harvestUtilitiesUsed()

harvestUtilitiesUsed
echo " Done 'harvestUtilitiesUsed' ..."

#more "${tmp}.utils"

rm -f "${tmp}_origin" \
	"${start}/${base}_originpkg" \
	"${start}/${base}_nopackage" \
	"${start}/${base}_nopath" \
	"${start}/${base}_norealpath" \
	"${start}/${base}_localcustom"		### this last file intended for future "classification"

while read line
do
	printf "\t Probing for '${line}' ...\n" >&2
	testorC=$( which ${line} 2>>/dev/null )
	if [ -n "${testorC}" ]
	then
		### originated from published code package
		### no related package; local custom coding
		testorP=$( realpath ${testorC} 2>>/dev/null )
		if [ -n "${testorP}" ]
		then
			#dpkg -S $( realpath ${testorC} ) 2>&1
			dpkg -S ${testorP} 2>&1
		else
			printf "NO_REALPATH: ${line}\n" >>"${start}/${base}_norealpath"
		fi
	else
		### no related package; not accessible from command line
		printf "NO_PATH: ${line}\n" >>"${start}/${base}_nopath"
	fi
done < "${tmp}.utils" > "${tmp}_origin"
#2>"${start}/${base}.localcustom"
grep    '^dpkg-query: no path' "${tmp}_origin" >"${start}/${base}_nopackage"
grep -v '^dpkg-query: no path' "${tmp}_origin" >"${start}/${base}_originpkg"

ls -l "${start}/${base}_"*

exit
exit
exit



testLoop()
{
	start=$(pwd)
	base=$(basename "$0" ".sh" )
	tmp=/tmp/${base}.tmp

	awk '{ print $NF }' <"${start}/${base}_nopackage" |
		while read line
		do
			printf "======================================================\n ${line}\n\n"
			#apt-file --ignore-case --regexp search "${line}"
			### search for string pattern in filenames only
			#apt-file --regexp search $( basename ${line} )'$'
			apt-file --regexp search $( basename ${line} )'$'

#			### search for string pattern in filenames only
#			apt-cache search $( basename ${line} )'$'
		done | tee zzzz
}
#testLoop

That is because nowadays /bin is softlinked to /usr/bin in several distributions (at least Ubuntu and probably debian too)

check:

user@computer:/$ realpath /bin
/usr/bin
user@computer:/$ ls -l /bin
lrwxrwxrwx 1 root root 7 jul  2  2023 /bin -> usr/bin

so 'which' and 'coreutils package' are both right.

1 Like