[Example Script] Script to find / search dependecies of shell script

About writing shell scripts and making the most of your shell
Forum rules
Topics in this forum are automatically closed 6 months after creation.
Locked
1000
Level 5
Level 5
Posts: 999
Joined: Wed Jul 29, 2020 2:14 am

[Example Script] Script to find / search dependecies of shell script

Post by 1000 »

Script

Code: Select all

#! /bin/bash 

# Licence: GNU GPL v3  https://www.gnu.org/licenses/gpl-3.0.html
# Version: 2
# Script use:	Name_of_script --check /path/to/next/script
# Destiny:      Script to find dependencies from *.bash and *.sh files




####################################{
## == Check Dependecies ==

# Check Dependecies
[[ -z $(which awk) ]] && DEP="$DEP"$'\n'"awk"
[[ -z $(which comm) ]] && DEP="$DEP"$'\n'"comm"
[[ -z $(which echo) ]] && DEP="$DEP"$'\n'"echo"
[[ -z $(which grep) ]] && DEP="$DEP"$'\n'"grep"
[[ -z $(which lsb_release) ]] && DEP="$DEP"$'\n'"lsb_release"
[[ -z $(which print) ]] && DEP="$DEP"$'\n'"print"
[[ -z $(which sed) ]] && DEP="$DEP"$'\n'"sed"
[[ -z $(which wc) ]] && DEP="$DEP"$'\n'"wc"
[[ -z $(which which) ]] && DEP="$DEP"$'\n'"which"
 
# End script if exist any error
[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}

## == Check Dependecies ==
####################################}




####################################{
## == Message library ==

NC='\e[0m'    # Reset Color
BL='\e[0;36m' # Cyan ECHO
GN='\e[0;32m' # Green ECHO
YW='\e[0;33m' # Yellow ECHO
RD='\e[0;31m' # Red ECHO


#----------------{
MESSAGE_DEBUG() {
    #DEBUG="OFF / ON"
    DEBUG="OFF"
    if [[ "$DEBUG" == "ON" ]] ; then
        echo -e "${BL}  DEBUG: $1 ${NC}"
    fi
}
#----------------}


MESSAGE_INFO() {
    #echo "----"
    echo -e "${GN}  INFO: $1 ${NC}"
    #echo "----"
}


MESSAGE_WARNING() {
    # Print text in red and redirect to error
	echo -e "${YW}  WARNING: $* ${NC}" 1>&2
    #; exit 1
}


MESSAGE_ERROR() {
    # Print text in red and redirect to error
	echo -e "${RD}  ERROR: $* ${NC}" 1>&2
    #; exit 1
}

## 
####################################}




CHECK_DEP() {
########################{
## == System settings ==

NAME_LINUX=$(lsb_release -is)
MESSAGE_DEBUG "--> Linux distribution name: $NAME_LINUX"


case "$NAME_LINUX" in
"UPLOS")
	FIND_PACKAGE="rpm -qf"   # find package name from path 
	FIND_SOURCE="rpm -qi"    # show info about package 
	;;
"Linuxmint")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    #   Example
    #   command:    dpkg-query -s ncurses-bin | grep -i source
    #               Source: ncurses
    #   command:    dpkg -s ncurses-bin | grep -i source
    #               Source: ncurses
    ;;
"Ubuntu")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    ;;
*)
    # Arch Linux
	#FIND_PACKAGE="pacman -Qo"

    MESSAGE_ERROR "Sorry, I don't have enough information about your system to develop this script."
    MESSAGE_INFO "You can send me output \" lsb_release -is \" , how find package name from path ,how show info about package "
    exit 1
    ;;
esac

## == System settings ==
########################}


MESSAGE_DEBUG "Used arguments: $*"


NAME_SCRIPT=$(awk '{ print $2 }' <<< "$*")
# Check if this is file
if [[ ! $(echo "$*" | wc -w) == "2" ]] ; then
                               MESSAGE_ERROR "Invalid number of arguments = $*" ; exit 1
elif [ ! -f "$NAME_SCRIPT" ]                                                          ; then
    MESSAGE_ERROR "We can't find file: ${NAME_SCRIPT}"       ; exit 1
elif ! grep -q "text" <(file "$NAME_SCRIPT")                                            ; then
    MESSAGE_ERROR "This is not text file: $NAME_SCRIPT"    ; exit 1
elif ! grep -q  "/bin/bash\|/bin/sh\|/usr/bin/env sh\|/usr/bin/env bash" "$NAME_SCRIPT" ; then
    MESSAGE_ERROR "This is not shell script: $NAME_SCRIPT" ; exit 1
fi




# All available commands in the system except reserved
# dictionary: https://www.gnu.org/software/bash/manual/html_node/Reserved-Word-Index.html
# compgen  https://bash.cyberciti.biz/bash-reference-manual/Programmable-Completion-Builtins.html#index-compgen-367

# From all available commands remove aliasses
LIST_1=$(comm  -23 <(compgen -c | sort) <(compgen -a | sort))
# From LIST_1 remove functions
LIST_2=$(comm  -23 <(echo "$LIST_1" | sort) <(compgen -A function | sort))
# From LIST_2 remove shell reserved
LIST_3=$(comm  -23 <(echo "$LIST_2" | sort) <(compgen -k | sort))
# From LIST_3 remove shell builtin commands and "[" and repeat
LIST_4=$(comm  -23 <(echo "$LIST_3" | sort) <(compgen -b | sort) | grep -v '\[' | uniq)
#   MESSAGE_DEBUG "List commands = $LIST_4"




# Clear file = remove comments, echo coments, options, gtkdialog comments, gtkdialog if(if false disable:CHECKBOX2), html, empty lines.
CLEARED_FILE=$(sed -e 's/#[^#]*//g' -e 's/"[^"]*"//g' -e 's/-[^-]*//g' -e '/<label>/d' -e '/able:/d' -e 's/<[^>]*>//g' -e '/^\s*$/d' ${NAME_SCRIPT})
#    MESSAGE_DEBUG "CLEARED_FILE = $CLEARED_FILE" 


while IFS= read -r COMMAND_LINE ; do
    if  $(echo "$CLEARED_FILE" | grep -wq "$COMMAND_LINE") ; then
			    #   MESSAGE_DEBUG "$COMMAND_LINE" 
			    LIST_OF_DEPEND=$(echo -e "$LIST_OF_DEPEND\n${COMMAND_LINE}")
    fi
done <<< "${LIST_4}"

# Remove first empty line
LIST_OF_DEPEND=$(grep -v -e '^$' <<< "$LIST_OF_DEPEND")

#    MESSAGE_DEBUG "LIST_OF_DEPEND = $LIST_OF_DEPEND"




# Find linux distribution dependencies
while IFS= read -r LINE_DEPEND ; do
        MESSAGE_DEBUG "LINE_DEPEND = $LINE_DEPEND"
	PATH_DEPEND=$(which "$LINE_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
		    MESSAGE_ERROR "We can not find path of command: $LINE_DEPEND" 
            continue
        fi


		NAME_PACK=$(${FIND_PACKAGE} "$PATH_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
            # Some symbolic links are badly made in packages and it will be harder to find the right package from package manager.
            # We will try use deep search 
            if grep -q "symbolic link" <(file "$PATH_DEPEND")  ;then
                MESSAGE_DEBUG   "This is symbolic link = $PATH_DEPEND"
                MESSAGE_WARNING "Search package from path failed. We will try find file from symbolic link = $PATH_DEPEND" 
                # We will try get file from symbolic link
                NAME_PACK=$(${FIND_PACKAGE} "$(readlink -f "$PATH_DEPEND")")
                    if [[ $(echo $?) == "1" ]] ;then
                        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                        continue
                    fi
            else
		        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                continue
            fi
        fi
        MESSAGE_DEBUG "NAME_PACK = $NAME_PACK"

		LIST_PACK=$(echo -e "${LIST_PACK}\n${NAME_PACK}")
done <<< "${LIST_OF_DEPEND}"




MESSAGE_DEBUG "==============="


# Remove first empty line, get first column
LIST_PACK=$(grep -v -e '^$' <<< "$LIST_PACK" | cut -d':' -f1 | sort | uniq)
MESSAGE_INFO "List of packages dependencies: \n$LIST_PACK"


echo " "
echo  "==============="
echo " "




##  Find source code
##  Poorly described packages in Ubuntu/LinuxMint and I don't know any other method 
##  so is off
#while IFS= read -r NAME_PACK ; do
#        SOURCE_CODE=$(${FIND_SOURCE} "$NAME_PACK" | grep -i source)
#	    MESSAGE_INFO "$NAME_PACK = $SOURCE_CODE"
#done <<< "${LIST_PACK}"




MESSAGE_INFO "Example of code for shell script:"
echo " "

echo '# Check Dependecies'
while IFS= read -r DEPEND ; do
    echo  "[[ -z \$(which "${DEPEND}") ]] && DEP=\"\$DEP\"$'\n'\"${DEPEND}\""
done <<< "${LIST_OF_DEPEND}"

echo " "
echo '# End script if exist any error'
echo '[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}'
echo " "
}




case $1 in
	"--help"|"-h")
		echo "---------------------------------------------------------"
		echo "usage: $0 --option script.sh"
		echo " "
		echo " Options:"
		echo "   -h  --help                 Show this help."
		echo "       --check                Check dependencies of shell script."
		echo " "
		echo "---------------------------------------------------------"
		exit 0
	;;
	"--check")
        CHECK_DEP "$*"
    ;;
	"--BUILD_CONFIG") 
        CHECK_DEP "$*"
        BUILD_CONFIG
	;;
    *)
        MESSAGE_ERROR "Unknow option. For help write: $0 --help"
    ;;
esac



Example Output:
$ bash find.dep.sh --check script.sh
dpkg-query: no matching path was found /usr/bin/awk
WARNING: Search package from path failed. We will try find file from symbolic link = /usr/bin/awk
dpkg-query: no matching path was found /usr/bin/which
WARNING: Search package from path failed. We will try find file from symbolic link = /usr/bin/which
INFO: List of packages dependencies:
coreutils
debianutils
gawk
grep
lsb-release
mime-support
sed


===============

INFO: Example of code for shell script:

# Check Dependecies
[[ -z $(which awk) ]] && DEP="$DEP"$'\n'"awk"
[[ -z $(which comm) ]] && DEP="$DEP"$'\n'"comm"
[[ -z $(which echo) ]] && DEP="$DEP"$'\n'"echo"
[[ -z $(which grep) ]] && DEP="$DEP"$'\n'"grep"
[[ -z $(which lsb_release) ]] && DEP="$DEP"$'\n'"lsb_release"
[[ -z $(which print) ]] && DEP="$DEP"$'\n'"print"
[[ -z $(which sed) ]] && DEP="$DEP"$'\n'"sed"
[[ -z $(which wc) ]] && DEP="$DEP"$'\n'"wc"
[[ -z $(which which) ]] && DEP="$DEP"$'\n'"which"

# End script if exist any error
[ -z "$DEP" ] || { echo " Error: Missing dependencies, before run script please install: $DEP" ; exit 1 ;}
Edit
- The example code in the example output is automatically generated from dependencies and you can use this code.
- I didn't use the package name for generating the sample code because any Linux distribution can split a package into any number of packages and any name.
Last edited by LockBot on Wed Dec 28, 2022 7:16 am, edited 1 time in total.
Reason: Topic automatically closed 6 months after creation. New replies are no longer allowed.
1000
Level 5
Level 5
Posts: 999
Joined: Wed Jul 29, 2020 2:14 am

Re: [Example Script] Script to find / search dependecies of shell script

Post by 1000 »

I corrected the script a bit.
- In the "--help" option I added more information that the script may not run perfectly.
- The script now can check dependencies from several files.

Code: Select all

#! /bin/bash 

# Licence: GNU GPL v3  https://www.gnu.org/licenses/gpl-3.0.html
# Version: 5
# Script use:	Name_of_script --check /path/to/next/script
# Destiny:      Script to find dependencies from *.bash and *.sh files




####################################{
## == Check Dependecies ==

# Check Dependecies (list created automatically by find.dep.sh)
[[ -z $(which awk) ]] && DEP="$DEP"$'\n'"awk"
[[ -z $(which comm) ]] && DEP="$DEP"$'\n'"comm"
[[ -z $(which echo) ]] && DEP="$DEP"$'\n'"echo"
[[ -z $(which find) ]] && DEP="$DEP"$'\n'"find"
[[ -z $(which grep) ]] && DEP="$DEP"$'\n'"grep"
[[ -z $(which lsb_release) ]] && DEP="$DEP"$'\n'"lsb_release"
[[ -z $(which print) ]] && DEP="$DEP"$'\n'"print"
[[ -z $(which sed) ]] && DEP="$DEP"$'\n'"sed"
[[ -z $(which wc) ]] && DEP="$DEP"$'\n'"wc"
[[ -z $(which which) ]] && DEP="$DEP"$'\n'"which"
 
# End script if exist any error
[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}


## == Check Dependecies ==
####################################}




####################################{
## == Message library ==

NC='\e[0m'    # Reset Color
BL='\e[0;36m' # Cyan ECHO
GN='\e[0;32m' # Green ECHO
YW='\e[0;33m' # Yellow ECHO
RD='\e[0;31m' # Red ECHO


#----------------{
MESSAGE_DEBUG() {
    #DEBUG="OFF / ON"
    DEBUG="OFF"
    if [[ "$DEBUG" == "ON" ]] ; then
        echo -e "${BL}  DEBUG: $1 ${NC}"
    fi
}
#----------------}


MESSAGE_INFO() {
    #echo "----"
    echo -e "${GN}  INFO: $1 ${NC}"
    #echo "----"
}


MESSAGE_WARNING() {
    # Print text in red and redirect to error
	echo -e "${YW}  WARNING: $* ${NC}" 1>&2
    #; exit 1
}


MESSAGE_ERROR() {
    # Print text in red and redirect to error
	echo -e "${RD}  ERROR: $* ${NC}" 1>&2
    #; exit 1
}

## 
####################################}




CHECK_ARG() {
########################{
## == System settings ==

NAME_LINUX=$(lsb_release -is)
MESSAGE_DEBUG "--> Linux distribution name: $NAME_LINUX"

case "$NAME_LINUX" in
"UPLOS")
	FIND_PACKAGE="rpm -qf"   # find package name from path 
	FIND_SOURCE="rpm -qi"    # show info about package 
	;;
"Linuxmint")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    #   Example
    #   command:    dpkg-query -s ncurses-bin | grep -i source
    #               Source: ncurses
    #   command:    dpkg -s ncurses-bin | grep -i source
    #               Source: ncurses
    ;;
"Ubuntu")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    ;;
*)
    # Arch Linux
	#FIND_PACKAGE="pacman -Qo"

    MESSAGE_ERROR "Sorry, I don't have enough information about your system to develop this script."
    MESSAGE_INFO "You can send me output \" lsb_release -is \" , how find package name from path ,how show info about package "
    exit 1
    ;;
esac

## == System settings ==
########################}


##################################{
## == Reject invalid arguments  ==

MESSAGE_DEBUG "Used arguments = $1"

##  Remove first word "--check" (skip first field and print the rest)
FILES=$(awk '{ $1="" ; print}' <<< "$1")
    MESSAGE_DEBUG "FILES = $FILES"

if [[ $(echo "$FILES" | wc -w) -lt "1" ]] ; then
    MESSAGE_ERROR "The number of arguments given is less than 1  = $FILES" ; exit 1
fi
for NAME_SCRIPT in $FILES ; do
    if [ ! -f "$NAME_SCRIPT" ]                                                              ; then
        MESSAGE_ERROR "We can't find file: ${NAME_SCRIPT}"     ; exit 1
    elif ! grep -q "text" <(file "$NAME_SCRIPT")                                            ; then
        MESSAGE_ERROR "This is not text file: $NAME_SCRIPT"    ; exit 1
    elif ! grep -q  "/bin/bash\|/bin/sh\|/usr/bin/env sh\|/usr/bin/env bash" "$NAME_SCRIPT" ; then
        MESSAGE_ERROR "This is not shell script: $NAME_SCRIPT" ; exit 1
    fi
done

## == Reject invalid arguments  ==
##################################}


#########################################{
## == List of all available commands   ==

# All available commands in the system except reserved
# dictionary: https://www.gnu.org/software/bash/manual/html_node/Reserved-Word-Index.html
# compgen  https://bash.cyberciti.biz/bash-reference-manual/Programmable-Completion-Builtins.html#index-compgen-367
#
# From all available commands remove aliasses
LIST_1=$(comm  -23 <(compgen -c | sort) <(compgen -a | sort))
# From LIST_1 remove functions
LIST_2=$(comm  -23 <(echo "$LIST_1" | sort) <(compgen -A function | sort))
# From LIST_2 remove shell reserved
LIST_3=$(comm  -23 <(echo "$LIST_2" | sort) <(compgen -k | sort))
# From LIST_3 remove shell builtin commands and "[" and repeat
LIST_4=$(comm  -23 <(echo "$LIST_3" | sort) <(compgen -b | sort) | grep -v '\[' | uniq)
#   MESSAGE_DEBUG "List commands = $LIST_4"

## == List of all available commands   ==
#########################################}


################################################################{
## == Test if script has commands from the list of commands   ==

for NAME_SCRIPT in $FILES ; do
    # Clear file = remove comments, echo coments, options, gtkdialog comments, gtkdialog if(if false disable:CHECKBOX2), html, empty lines.
    CLEARED_FILE=$(sed -e 's/#[^#]*//g' -e 's/"[^"]*"//g' -e 's/-[^-]*//g' -e '/<label>/d' -e '/able:/d' -e 's/<[^>]*>//g' -e '/^\s*$/d' -e "s/'[^']*'//g" ${NAME_SCRIPT})
        MESSAGE_DEBUG "==============="
        MESSAGE_DEBUG "CLEARED_FILE = $CLEARED_FILE" 
        MESSAGE_DEBUG "==============="

    #CLEARED_FILE=$(echo "$CLEARED_FILE" | sort | uniq)

    # Test if your script has any command from list of commands
    while IFS= read -r COMMAND_LINE ; do
        if  $(echo "$CLEARED_FILE" | grep -wq "$COMMAND_LINE") ; then
			    #   MESSAGE_DEBUG "$COMMAND_LINE" 
			    LIST_OF_DEPEND=$(echo -e "$LIST_OF_DEPEND\n${COMMAND_LINE}")
        fi
    done <<< "${LIST_4}"


    LIST_OF_DEPEND_ALL=$(echo -e "$LIST_OF_DEPEND_ALL\n${LIST_OF_DEPEND}")
#       MESSAGE_DEBUG "$LIST_OF_DEPEND_ALL"

done
## == Test if script has commands from the list of commands   ==
################################################################}

#exit
# Remove empty lines, sort, and remove repetitions
LIST_OF_DEPEND_ALL=$(grep -v -e '^$' <<< "$LIST_OF_DEPEND_ALL" | sort | uniq)
       MESSAGE_DEBUG "LIST_OF_DEPEND_ALL = $LIST_OF_DEPEND_ALL"


################################################{
## == Find which package contains the command ==
while IFS= read -r LINE_DEPEND ; do
        MESSAGE_DEBUG "LINE_DEPEND = $LINE_DEPEND"
	PATH_DEPEND=$(which "$LINE_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
		    MESSAGE_ERROR "We can not find path of command: $LINE_DEPEND" 
            continue
        fi


		NAME_PACK=$(${FIND_PACKAGE} "$PATH_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
            # Some symbolic links are badly made in packages and it will be harder to find the right package from package manager.
            # We will try use deep search 
            if grep -q "symbolic link" <(file "$PATH_DEPEND")  ;then
                MESSAGE_DEBUG   "This is symbolic link = $PATH_DEPEND"
                MESSAGE_WARNING "Search package from path failed. We will try find file from symbolic link = $PATH_DEPEND" 
                # We will try get file from symbolic link
                NAME_PACK=$(${FIND_PACKAGE} "$(readlink -f "$PATH_DEPEND")")
                    if [[ $(echo $?) == "1" ]] ;then
                        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                        continue
                    fi
            else
		        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                continue
            fi
        fi
        MESSAGE_DEBUG "NAME_PACK = $NAME_PACK"

		LIST_PACK=$(echo -e "${LIST_PACK}\n${NAME_PACK}")
done <<< "${LIST_OF_DEPEND_ALL}"
## == Find which package contains the command ==
################################################}


#MESSAGE_DEBUG "$LIST_PACK"


#################################{
## == Main information to show ==
LIST_PACK=$(grep -v -e '^$' <<< "$LIST_PACK" | sort)
MESSAGE_INFO "List of packages dependencies: \n$LIST_PACK"

# Remove empty lines and sort
LIST_ONLY_PACK=$(echo "$LIST_PACK" | cut -d':' -f1 | sort | uniq)
MESSAGE_DEBUG "Short form of dependencies: \n$LIST_ONLY_PACK"

echo " "
echo  "==============="
echo " "


##############################{
## == Find source code name ==
function DISABLED() {
##  Find source code
##  Poorly described packages in Ubuntu/LinuxMint and I don't know any other method 
##  so is off
while IFS= read -r NAME_ONLY_PACK ; do
        SOURCE_CODE=$(${FIND_SOURCE} "$NAME_ONLY_PACK" | grep -i source)
	    MESSAGE_INFO "$NAME_ONLY_PACK = $SOURCE_CODE"
done <<< "${LIST_ONLY_PACK}"

echo " "
echo  "==============="
echo " "
}
## == Find source code name ==
##############################}


MESSAGE_INFO 'If your script does not work on another system, \nyou can check / compare aliases with command:  alias -p \n and check inside script.'
echo " "
echo  "==============="

echo " "
MESSAGE_INFO "Example of the code for your shell script:"
echo " "

echo "# Check Dependecies (list created automatically by $0)"
while IFS= read -r DEPEND ; do
    echo  "[[ -z \$(which "${DEPEND}") ]] && DEP=\"\$DEP\"$'\n'\"${DEPEND}\""
done <<< "${LIST_OF_DEPEND_ALL}"

echo " "
echo '# End script if exist any error'
echo '[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}'
echo " "
echo  '#==============='

## == Main information to show ==
#################################}
}




case $1 in
	"--help"|"-h")
		echo "---------------------------------------------------------"
        echo ' '
		echo '  Destiny:        Script to find dependencies from *.bash and *.sh files'
		echo "  Script usage:   $0 --check file.1.sh file.2.sh"
        echo '                  This script can handle one or more files simultaneously.'
		echo " "
		echo " Options:"
		echo "   -h  --help             Show this help."
		echo "       --check            Checks dependencies. Use this option with your script."
		echo " "
		echo "---------------------------------------------------------"
        echo "  Info:"
		echo " "
		echo "1. This script has a debug option, if you want enable debugging"
		echo ' you have to inside the script find line containing:'
        echo 'DEBUG="OFF" and change to DEBUG="ON"'
		echo " "
        echo '2. This script is not very fast.'
        echo ' '
		echo '3. I did not use the package name for generating the example code '
		echo ' because any Linux distribution can split a package into any number of packages and any name.'
		echo " "
		echo '4. For now it was hard for me to filter out all the comments created with "echo" from any script. '
		echo ' So script output may contain the redundant dependencies if you mentioned in the comment'
		echo ' For example: script , compare , split'
		echo ' So if the dependencies are wrong, you have to delete them manually'
		echo ' You can find and check in this script: CLEARED_FILE'
        echo ' '
        echo '5. Dependencies of coreutils may seem redundant,'
        echo ' but may exist different versions of them. (coreutils, busybox, toybox)'
        echo ' '
		exit 0
	;;
	"--check")
        CHECK_ARG "$*"
    ;;
    *)
        MESSAGE_ERROR "Unknow option. For help write: $0 --help"
    ;;
esac

User avatar
Termy
Level 12
Level 12
Posts: 4254
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: [Example Script] Script to find / search dependecies of shell script

Post by Termy »

I hate to be that guy right now, however, BASH itself can check with one of at least two ways, when searching PATH: type -P DEP or command -v DEP where the which(1) command isn't really needed. The PATH-checking part of this script, while I understand is probably for educational reasons (I have and had projects like that, too) adds an unnecessary layer of execution.

My go-to method of dependency checking is using the type builtin, because it's more efficient and portable than using which(1). Typically, I'll use a short for loop. Take this excerpt from cito(8), for example:

Code: Select all

DepCount=0
for Dep in mv id chmod chown md5sum mkdir date rm mktemp; do
	if ! command -v "$Dep" 1> /dev/null 2>&1; then
		Err 0 "Dependency '$Dep' not met."
		DepCount=$((DepCount + 1))
	fi
done

[ $DepCount -gt 0 ] && exit 1
Where Err() refers to this function, or variations thereof:

Code: Select all

Err(){
    printf 'ERROR: %s\n' "$2" 1>&2
    [ $1 -gt 0 ] && exit $1
}
Note, that cito(8) is a Bourne Shell program, but here's my BASH version, taken from glkfu(1):

Code: Select all

DepCount=0
for Dep in glkfu-list sync make tar rm cp sha256sum; do
	if ! type -P "$Dep" &> /dev/null; then
		Err 0 "Dependency '$Dep' not met."
		let DepCount++
	fi
done

[ $DepCount -gt 0 ] && exit 1
Both are very similar, however.

I don't type those blocks out each and every time, although I used to, because I use my own snippets in vim(1).

Lastly, I see this done a lot, but it's actually redundant:

Code: Select all

[[ -z $(which wc) ]] && DEP="$DEP"$'\n'"wc"
Let's walk through what's going on here.

* The [[ command is used, checking for the returned value of the given command substitution ($() or ``) being empty.
* The which(1) command is just returning one or more locations at which a given executable in $PATH should be found.
* If the test is successful (&& -- logical and operator), then the shell declares a variable and assigns it a value.

That's it broken down, minus the irrelevant stuff (the value assigned to the variable).

So where this is redundant, is that which(1) is already checking the PATH for the executable, and for something to be valid for PATH, it must be an executable of type file, yet you're checking for the already executable file being executable; does that make sense?

Don't worry, you're not the only one to do this. I sent a PR to a project just last night called bash-it which had a lot of similar redundancies. Be careful from whom you learn, because you can pick up a lot of bad habits. If ever in doubt, break it down yourself, following documentation if you need, and arrive at your own conclusion.

So, a cleaner and more efficient approach would be:

Code: Select all

type -P wc &> /dev/null && DEP="$DEP"$'\n'"wc"
Also, I'm guessing you don't need to use the $'\n' trick here, so you can probably get away with: DEP="$DEP\nwc" This would greatly improve readability. In 6 or so years of considerable shell programming, I can honestly say I've needed $'\n' or similar maybe one or twice, in some pretty weird edge-cases.
I'm also Terminalforlife on GitHub.
1000
Level 5
Level 5
Posts: 999
Joined: Wed Jul 29, 2020 2:14 am

Re: [Example Script] Script to find / search dependecies of shell script

Post by 1000 »

type -P DEP or command -v

Indeed are builtin :D

Code: Select all

$ compgen -b | grep type
type
typeset

$ compgen -b | grep command
command

Command "type" works better.

Code: Select all

$ type -P pwd
/bin/pwd

$ command -v pwd
pwd

Recently I read about the danger of having an error in a script.
Sometimes, if there is an error in the script, the next command is executed.
This next command could be "rm" for example.
The script doesn't know it's in the wrong directory and may damage our system.

My test shows that the script will not be executed even if the which command does not exist.
Although I did not foresee it.

Code: Select all

...
dep: line 10: whichf: command not found 
 Error: Missing dependencies, before run script please install:
...
This is good.


In my case performance "type -P" and "which" is identical.

I didn't know a good replacement for the "which" command before.
If there will an error at the beginning of the script while checking dependencies, that's better than halfway through the script.

The advantages are undeniable.
Command "type -P" it's a very good replacement :D

Code: Select all

$ DEP=KK ; DEP="$DEP\nwc" ; echo "$DEP"
KK\nwc

$ DEP=KK ; DEP="$DEP"$'\n'"wc" ; echo "$DEP"
KK
wc

$ DEP=KK ; DEP=$(echo -e "$DEP\nwc") ; echo "$DEP"
KK
wc

$ DEP=KK ; DEP=$(printf "$DEP\nwc") ; echo "$DEP"
KK
wc

Corrected code

Code: Select all

#! /bin/bash 

# Licence: GNU GPL v3  https://www.gnu.org/licenses/gpl-3.0.html
# Version: 6
# Script use:	Name_of_script --check /path/to/next/script
# Destiny:      Script to find dependencies from *.bash and *.sh files




####################################{
## == Check Dependecies ==
[[ -z $(type -P awk) ]] && DEP="$DEP"$'\n'"awk"
[[ -z $(type -P comm) ]] && DEP="$DEP"$'\n'"comm"
[[ -z $(type -P cut) ]] && DEP="$DEP"$'\n'"cut"
[[ -z $(type -P echo) ]] && DEP="$DEP"$'\n'"echo"
[[ -z $(type -P grep) ]] && DEP="$DEP"$'\n'"grep"
[[ -z $(type -P lsb_release) ]] && DEP="$DEP"$'\n'"lsb_release"
[[ -z $(type -P sed) ]] && DEP="$DEP"$'\n'"sed"
[[ -z $(type -P wc) ]] && DEP="$DEP"$'\n'"wc"

# End script if exist any error
[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}

## == Check Dependecies ==
####################################}




####################################{
## == Message library ==

NC='\e[0m'    # Reset Color
BL='\e[0;36m' # Cyan ECHO
GN='\e[0;32m' # Green ECHO
YW='\e[0;33m' # Yellow ECHO
RD='\e[0;31m' # Red ECHO


#----------------{
MESSAGE_DEBUG() {
    #DEBUG="OFF / ON"
    DEBUG="OFF"
    if [[ "$DEBUG" == "ON" ]] ; then
        echo -e "${BL}  DEBUG: $1 ${NC}"
    fi
}
#----------------}


MESSAGE_INFO() {
    #echo "----"
    echo -e "${GN}  INFO: $1 ${NC}"
    #echo "----"
}


MESSAGE_WARNING() {
    # Print text in red and redirect to error
	echo -e "${YW}  WARNING: $* ${NC}" 1>&2
    #; exit 1
}


MESSAGE_ERROR() {
    # Print text in red and redirect to error
	echo -e "${RD}  ERROR: $* ${NC}" 1>&2
    #; exit 1
}

## 
####################################}




CHECK_ARG() {
########################{
## == System settings ==

NAME_LINUX=$(lsb_release -is)
MESSAGE_DEBUG "--> Linux distribution name: $NAME_LINUX"

case "$NAME_LINUX" in
"UPLOS")
	FIND_PACKAGE="rpm -qf"   # find package name from path 
	FIND_SOURCE="rpm -qi"    # show info about package 
	;;
"Linuxmint")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    #   Example
    #   command:    dpkg-query -s ncurses-bin | grep -i source
    #               Source: ncurses
    #   command:    dpkg -s ncurses-bin | grep -i source
    #               Source: ncurses
    ;;
"Ubuntu")
	FIND_PACKAGE="dpkg -S"   # find package name from path 
	FIND_SOURCE="dpkg-query -s"    # show info about package 
    ;;
*)
    # Arch Linux
	#FIND_PACKAGE="pacman -Qo"

    MESSAGE_ERROR "Sorry, I don't have enough information about your system to develop this script."
    MESSAGE_INFO "You can send me output \" lsb_release -is \" , how find package name from path ,how show info about package "
    exit 1
    ;;
esac

## == System settings ==
########################}


##################################{
## == Reject invalid arguments  ==

MESSAGE_DEBUG "Used arguments = $1"

##  Remove first word "--check" (skip first field and print the rest)
FILES=$(awk '{ $1="" ; print}' <<< "$1")
    MESSAGE_DEBUG "FILES = $FILES"

if [[ $(echo "$FILES" | wc -w) -lt "1" ]] ; then
    MESSAGE_ERROR "The number of arguments given is less than 1  = $FILES" ; exit 1
fi
for NAME_SCRIPT in $FILES ; do
    if [ ! -f "$NAME_SCRIPT" ]                                                              ; then
        MESSAGE_ERROR "We can't find file: ${NAME_SCRIPT}"     ; exit 1
    elif ! grep -q "text" <(file "$NAME_SCRIPT")                                            ; then
        MESSAGE_ERROR "This is not text file: $NAME_SCRIPT"    ; exit 1
    elif ! grep -q  "/bin/bash\|/bin/sh\|/usr/bin/env sh\|/usr/bin/env bash" "$NAME_SCRIPT" ; then
        MESSAGE_ERROR "This is not shell script: $NAME_SCRIPT" ; exit 1
    fi
done

## == Reject invalid arguments  ==
##################################}


#########################################{
## == List of all available commands   ==

# All available commands in the system except reserved
# dictionary: https://www.gnu.org/software/bash/manual/html_node/Reserved-Word-Index.html
# compgen  https://bash.cyberciti.biz/bash-reference-manual/Programmable-Completion-Builtins.html#index-compgen-367
#
# From all available commands remove aliasses
LIST_1=$(comm  -23 <(compgen -c | sort) <(compgen -a | sort))
# From LIST_1 remove functions
LIST_2=$(comm  -23 <(echo "$LIST_1" | sort) <(compgen -A function | sort))
# From LIST_2 remove shell reserved
LIST_3=$(comm  -23 <(echo "$LIST_2" | sort) <(compgen -k | sort))
# From LIST_3 remove shell builtin commands and "[" and repeat
LIST_4=$(comm  -23 <(echo "$LIST_3" | sort) <(compgen -b | sort) | grep -v '\[' | uniq)
#   MESSAGE_DEBUG "List commands = $LIST_4"

## == List of all available commands   ==
#########################################}


################################################################{
## == Test if script has commands from the list of commands   ==

for NAME_SCRIPT in $FILES ; do
    # Clear file = remove comments, echo coments, options, gtkdialog comments, gtkdialog if(if false disable:CHECKBOX2), html, empty lines.
    CLEARED_FILE=$(sed -e 's/#[^#]*//g' -e 's/"[^"]*"//g' -e "s/'[^']*'//g" -e 's/-[^-]*//g' -e '/<label>/d' -e '/able:/d' -e 's/<[^>]*>//g' -e '/^\s*$/d'  ${NAME_SCRIPT})
        MESSAGE_DEBUG "==============="
        MESSAGE_DEBUG "CLEARED_FILE = $CLEARED_FILE" 
        MESSAGE_DEBUG "==============="

    CLEARED_FILE=$(echo "$CLEARED_FILE" | sort | uniq)

    # Test if your script has any command from list of commands
    while IFS= read -r COMMAND_LINE ; do
        if  $(echo "$CLEARED_FILE" | grep -wq "$COMMAND_LINE") ; then
			    #   MESSAGE_DEBUG "$COMMAND_LINE" 
			    LIST_OF_DEPEND=$(echo -e "$LIST_OF_DEPEND\n${COMMAND_LINE}")
        fi
    done <<< "${LIST_4}"


    LIST_OF_DEPEND_ALL=$(echo -e "$LIST_OF_DEPEND_ALL\n${LIST_OF_DEPEND}")
#       MESSAGE_DEBUG "$LIST_OF_DEPEND_ALL"

done
## == Test if script has commands from the list of commands   ==
################################################################}


# Remove empty lines, sort, and remove repetitions
LIST_OF_DEPEND_ALL=$(grep -v -e '^$' <<< "$LIST_OF_DEPEND_ALL" | sort | uniq)
       MESSAGE_DEBUG "LIST_OF_DEPEND_ALL = $LIST_OF_DEPEND_ALL"


################################################{
## == Find which package contains the command ==
while IFS= read -r LINE_DEPEND ; do
        MESSAGE_DEBUG "LINE_DEPEND = $LINE_DEPEND"
	PATH_DEPEND=$(type -P "$LINE_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
		    MESSAGE_ERROR "We can not find path of command: $LINE_DEPEND" 
            continue
        fi


		NAME_PACK=$(${FIND_PACKAGE} "$PATH_DEPEND")
	    if [[ $(echo $?) == "1" ]] ;then
            # Some symbolic links are badly made in packages and it will be harder to find the right package from package manager.
            # We will try use deep search 
            if grep -q "symbolic link" <(file "$PATH_DEPEND")  ;then
                MESSAGE_DEBUG   "This is symbolic link = $PATH_DEPEND"
                MESSAGE_WARNING "Search package from path failed. We will try find file from symbolic link = $PATH_DEPEND" 
                # We will try get file from symbolic link
                NAME_PACK=$(${FIND_PACKAGE} "$(readlink -f "$PATH_DEPEND")")
                    if [[ $(echo $?) == "1" ]] ;then
                        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                        continue
                    fi
            else
		        MESSAGE_ERROR "We can not find package = $PATH_DEPEND" 
                continue
            fi
        fi
        MESSAGE_DEBUG "NAME_PACK = $NAME_PACK"

		LIST_PACK=$(echo -e "${LIST_PACK}\n${NAME_PACK}")
done <<< "${LIST_OF_DEPEND_ALL}"
## == Find which package contains the command ==
################################################}


#MESSAGE_DEBUG "$LIST_PACK"


#################################{
## == Main information to show ==
LIST_PACK=$(grep -v -e '^$' <<< "$LIST_PACK" | sort)
MESSAGE_INFO "List of packages dependencies: \n$LIST_PACK"

# Remove empty lines and sort
LIST_ONLY_PACK=$(echo "$LIST_PACK" | cut -d':' -f1 | sort | uniq)
MESSAGE_DEBUG "Short form of dependencies: \n$LIST_ONLY_PACK"

echo " "
echo  "==============="
echo " "


##############################{
## == Find source code name ==
function DISABLED() {
##  Find source code
##  Poorly described packages in Ubuntu/LinuxMint and I don't know any other method 
##  so is off
while IFS= read -r NAME_ONLY_PACK ; do
        SOURCE_CODE=$(${FIND_SOURCE} "$NAME_ONLY_PACK" | grep -i source)
	    MESSAGE_INFO "$NAME_ONLY_PACK = $SOURCE_CODE"
done <<< "${LIST_ONLY_PACK}"

echo " "
echo  "==============="
echo " "
}
## == Find source code name ==
##############################}


MESSAGE_INFO 'If your script does not work on another system, \nyou can check / compare aliases with command:  alias -p \n and check inside script.'
echo " "
echo  "==============="

echo " "
MESSAGE_INFO "Example of the code for your shell script:"
echo " "

echo "# Check Dependecies (list created automatically by $0)"
while IFS= read -r DEPEND ; do
    echo  "[[ -z \$(type -P "${DEPEND}") ]] && DEP=\"\$DEP\"$'\n'\"${DEPEND}\""
done <<< "${LIST_OF_DEPEND_ALL}"

echo " "
echo '# End script if exist any error'
echo '[ -z "$DEP" ] || { echo "   Error: Missing dependencies, before run script please install: $DEP"  ; exit 1  ;}'
echo " "
echo  '#==============='

## == Main information to show ==
#################################}
}




case $1 in
	"--help"|"-h")
		echo "---------------------------------------------------------"
        echo ' '
		echo '  Destiny:        Script to find dependencies from *.bash and *.sh files'
		echo "  Script usage:   $0 --check file.1.sh file.2.sh"
        echo '                  This script can handle one or more files simultaneously.'
		echo " "
		echo " Options:"
		echo "   -h  --help             Show this help."
		echo "       --check            Checks dependencies. Use this option with your script."
		echo " "
		echo "---------------------------------------------------------"
        echo "  Info:"
		echo " "
		echo "1. This script has a debug option, if you want enable debugging"
		echo ' you have to inside the script find line containing:'
        echo 'DEBUG="OFF" and change to DEBUG="ON"'
		echo " "
        echo '2. This script is not very fast.'
        echo ' '
		echo '3. I did not use the package name for generating the example code '
		echo ' because any Linux distribution can split a package into any number of packages and any name.'
		echo " "
		echo '4. For now it was hard for me to filter out all the comments created with "echo" from any script. '
		echo ' So script output may contain the redundant dependencies if you mentioned in the comment'
		echo ' For example: script , compare , split'
		echo ' So if the dependencies are wrong, you have to delete them manually'
		echo ' You can find and check in this script: CLEARED_FILE'
        echo ' '
        echo '5. Dependencies of coreutils may seem redundant,'
        echo ' but may exist different versions of them. (coreutils, busybox, toybox)'
        echo ' '
		exit 0
	;;
	"--check")
        CHECK_ARG "$*"
    ;;
    *)
        MESSAGE_ERROR "Unknow option. For help write: $0 --help"
    ;;
esac

Edit
Thanks! Any help or reasonable criticism is appreciated. :D

Edit
I created script with name sh.sh

Code: Select all

#!/bin/sh
DEP=KK ; DEP="$DEP"$'\n'"wc" ; echo "$DEP"

Code: Select all

$ bash sh.sh
KK
wc

$ sh sh.sh
KK$
wc
Then I edit to

Code: Select all

#!/bin/sh
DEP=KK ; DEP="$DEP\nwc" ; echo "$DEP"
And

Code: Select all

$ sh sh.sh
KK
wc

$ bash sh.sh
KK\nwc
So that's the reason why it doesn't work for me.
User avatar
Termy
Level 12
Level 12
Posts: 4254
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: [Example Script] Script to find / search dependecies of shell script

Post by Termy »

1000 wrote: Mon Feb 22, 2021 3:18 am So that's the reason why it doesn't work for me.
It depends how you're accessing the variable. In your case, you probably have the echo builtin configured to not expand escape sequences by default; mine does, akin to Bourne Shell. Use the printf builtin like this, to see what I mean, using %b to expand escape sequences within position one:

Code: Select all

printf '%b' "$DEP"
Or use echo:

Code: Select all

echo -e "$DEP"
I tend to discourage the use of echo in Shell programs; it's not as consistent or feature-rich as printf.
1000 wrote: Mon Feb 22, 2021 3:18 am Command "type" works better.
The thing is, the output isn't important (at least for a standard $PATH dependency check), the exit status is. For example:

Code: Select all

#!/bin/bash

type -P "$DEP" &> /dev/null || exit 1
That script will exit with a status of 1. No output is tested, nor is it needed to be tested, because the point of a dependency check like this, is to determine if the file is an executable of type file in $PATH.
1000 wrote: Mon Feb 22, 2021 3:18 am Recently I read about the danger of having an error in a script.
Sometimes, if there is an error in the script, the next command is executed.
This next command could be "rm" for example.
The script doesn't know it's in the wrong directory and may damage our system.

My test shows that the script will not be executed even if the which command does not exist.
Although I did not foresee it.
And that's true, if you don't program accordingly. Here's another example:

Code: Select all

#!/bin/bash

if type -P FILE &> /dev/null; then
	LIST
fi
In the above, LIST will not execute unless FILE is a valid executable in $PATH.
1000 wrote: Mon Feb 22, 2021 3:18 am In my case performance "type -P" and "which" is identical.
Here's my quick test on an i5 4690K CPU, plenty of RAM, and using an SSD (on which Firefox, BASH, and which(1) is installed):

Code: Select all

$ time which firefox
/usr/bin/firefox
>>> real 0.001 | user 0.000 | sys 0.001 | pcpu 105.07 <<<
$ time type -P firefox
/usr/bin/firefox
>>> real 0.000 | user 0.000 | sys 0.000 | pcpu 98.36 <<<
Execute them however many times until you're comfortable with the result being a good representation of the difference between the two. I know it's a very small difference, so it's up to you whether that matters or not. These things combined do add up though, especially in larger projects, and on slower and/or heavy-load machines.
I'm also Terminalforlife on GitHub.
1000
Level 5
Level 5
Posts: 999
Joined: Wed Jul 29, 2020 2:14 am

Re: [Example Script] Script to find / search dependecies of shell script

Post by 1000 »

The thing is, the output isn't important, the exit status is.
Now I understand what you mean.
I used the which command mainly in two places.

1. In the middle: something like:
dpkg -S $(type -P pwd)

2. And at the end: something like:
echo "[[ -z \$(type -P "pwd") ]] && DEP=\"\$DEP\"$'\n'pwd"

This is true, in both cases the output about error is not important as we have our own error message.
However, I like to see more information about errors in this case.
This can be helpful if my comment will not be sufficient for user.
____________________

I am not for it to end the program immediately when there is no dependency.
A long time ago I was compiling programs a bit.
If not all, maybe most "./configure" scripts works the same. Stops the script immediately.
Then you need install the dependency and run "./configure" script again.
And you will do it a couple of times if a lot of installed dependencies are missing.
In my opinion it is a waste our of time and annoying because boring.
I believe that my solution is better.
My "example" in script checks all dependencies and then finally shows what is missing.
You can immediately see all the missing dependencies and do not have to run the script again.
-------------------------------
$ time which firefox
/usr/bin/firefox
>>> real 0.001 | user 0.000 | sys 0.001 | pcpu 105.07 <<<
I don't have "pcpu"
Do you mean R option in time command?

Code: Select all

$ /usr/bin/time -f "C: %C \n D: %D \
>  \n E: %E \n F: %F \n I: %I \n K: %K \
> \n M: %M \n O: %O \n P: %P \n R: %R \n S: %S \
> \n U: %U \n W: %W \n X: %X \n Z: %Z \n c: %c \
> \n e: %e \n k: %k \n p: %p \n r: %r \n s: %s \n t: %t \n w: %w \n x: %x" time which firefox
/usr/bin/firefox
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 1836maxresident)k
0inputs+0outputs (0major+76minor)pagefaults 0swaps
C: time which firefox 
 D: 0  
 E: 0:00.00 
 F: 0 
 I: 0 
 K: 0 
 M: 1836 
 O: 0 
 P: 100% 
 R: 155 
 S: 0.00 
 U: 0.00 
 W: 0 
 X: 0 
 Z: 4096 
 c: 0 
 e: 0.00 
 k: 0 
 p: 0 
 r: 0 
 s: 0 
 t: 0 
 w: 3 
 x: 0
However I suggest test time my_script --check next_script with "which" and next with "type -P" command.
The bigger the test is the bigger the time difference.
You will see that " real " is very similar, minor changes are a measurement error, i.e. interference in the measurement.
Last edited by 1000 on Tue Feb 23, 2021 11:21 am, edited 1 time in total.
User avatar
Termy
Level 12
Level 12
Posts: 4254
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: [Example Script] Script to find / search dependecies of shell script

Post by Termy »

I'm enjoying these posts. :) Talking about Shell should be a hobby of its own. :lol:
1000 wrote: Tue Feb 23, 2021 11:10 am If not all, maybe most "./configure" scripts works the same. Stops the script immediately.
Then you need install the dependency and run "./configure" script again.
Agreed -- that bugs me too, which is why my usual approach tells the user about all missing dependencies before exiting, such as the for loops I showed you from cito(8) and glkfu(1).
1000 wrote: Tue Feb 23, 2021 11:10 am The bigger the test is the bigger the time difference.
You will see that " real " is very similar, minor changes are a measurement error, i.e. interference in the measurement.
I agree about the size of the test yielding clearer results, but I disagree that this is just down to interference, because you can consistently see a difference, and you can compare other things which won't show that claimed interference, such as type -P firefox VS command -v firefox. Besides, it makes absolute sense for it to be slower using which(1), since it's a separate program, whereas those builtins are built into BASH. I have a feeling we're going to agree to disagree, here. :P
1000 wrote: Tue Feb 23, 2021 11:10 am dpkg -S $(type -P pwd)
I'd probably be tempted to use which(1) there too, if it were just a quick one-liner on the terminal. :lol:
I'm also Terminalforlife on GitHub.
Locked

Return to “Scripts & Bash”