Thank you, Eugene, but that does not report a unique package for a specified command.
For example, "dd" gives the following:
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".
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