481 lines
10 KiB
Bash
Executable file
481 lines
10 KiB
Bash
Executable file
#!/bin/bash -e
|
|
|
|
#
|
|
# Towel: Randomly Useful Bash Functions
|
|
# Never Leave Home Without Your Towel
|
|
#
|
|
# Copyright (C) 2019 Scott Duensing <scott@kangaroopunch.com>
|
|
#
|
|
# This software is provided 'as-is', without any express or implied
|
|
# warranty. In no event will the authors be held liable for any damages
|
|
# arising from the use of this software.
|
|
#
|
|
# Permission is granted to anyone to use this software for any purpose,
|
|
# including commercial applications, and to alter it and redistribute it
|
|
# freely, subject to the following restrictions:
|
|
#
|
|
# 1. The origin of this software must not be misrepresented; you must not
|
|
# claim that you wrote the original software. If you use this software
|
|
# in a product, an acknowledgment in the product documentation would be
|
|
# appreciated but is not required.
|
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
|
# misrepresented as being the original software.
|
|
# 3. This notice may not be removed or altered from any source distribution.
|
|
#
|
|
|
|
|
|
#
|
|
# Color constants for tBoldBox.
|
|
#
|
|
tRED=1
|
|
tGREEN=2
|
|
tYELLOW=3
|
|
tBLUE=4
|
|
tPURPLE=5
|
|
tCYAN=6
|
|
|
|
|
|
#
|
|
# Global variables used by Towel.
|
|
#
|
|
__tG__PARENTPATH="${BASH_SOURCE[0]}"
|
|
__tG__SUDOPASS=
|
|
|
|
|
|
#
|
|
# Argument helper to process things intended for Towel.
|
|
#
|
|
# Parameters:
|
|
# ARGS - All command line arguments passed to script.
|
|
#
|
|
function tArgsHandler() {
|
|
local __FIRST=$1
|
|
|
|
# Is SUDO after us?
|
|
if [[ "${__FIRST}" == "TOWEL_ASKPASS" ]]; then
|
|
tSudoAskPass # Does not return.
|
|
fi
|
|
}
|
|
|
|
|
|
#
|
|
# Draws bold white text inside a colored box.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# COLOR - Towel color constant for box background color.
|
|
# TEXT - Text to display.
|
|
#
|
|
function tBoldBox() {
|
|
local __COLOR=$1
|
|
local __TEXT=$2
|
|
local __LEN=$((${#__TEXT}+6))
|
|
local __SPACES=$(printf "%${__LEN}s")
|
|
|
|
__COLOR=${!__COLOR}
|
|
|
|
echo -e "\e[1m\e[4${__COLOR}m${__SPACES}\e[0m"
|
|
echo -e "\e[1m\e[4${__COLOR}m ${__TEXT} \e[0m"
|
|
echo -e "\e[1m\e[4${__COLOR}m${__SPACES}\e[0m"
|
|
}
|
|
|
|
|
|
#
|
|
# Checks to see if listed packages have been installed.
|
|
# Currently DEB packages only.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the packages that are missing.
|
|
# VAR - List of packages to check, sparated by spaces.
|
|
#
|
|
function tCheckPackages() {
|
|
local __RESULT=$1
|
|
local __PACKAGES=${*:2}
|
|
local __MISSING=
|
|
local __P=
|
|
|
|
set +e
|
|
for __P in ${__PACKAGES}; do
|
|
dpkg-query -s ${__P} 2> /dev/null | grep -q ^"Status: install ok installed"$
|
|
if [ "$?x" == "1x" ]; then
|
|
__MISSING="${__MISSING} ${__P}"
|
|
fi
|
|
done
|
|
set -e
|
|
|
|
tTrimL __MISSING "${__MISSING}"
|
|
|
|
eval $__RESULT=\${__MISSING}
|
|
}
|
|
|
|
|
|
#
|
|
# Downloads file.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# URL - URL of file to download.
|
|
# FILE - Optional alternate name to save downloaded file under.
|
|
#
|
|
function tDownload() {
|
|
local __URL=$1
|
|
local __FILE=
|
|
|
|
tTrim __FILE "$2"
|
|
|
|
if [[ -z ${__FILE} ]]; then
|
|
__FILE=${__URL##*/}
|
|
fi
|
|
|
|
if [[ -e "${__FILE}" ]]; then
|
|
rm -f "${__FILE}"
|
|
fi
|
|
|
|
wget -O "${__FILE}" ${__URL}
|
|
}
|
|
|
|
|
|
#
|
|
# Escapes a given string so it's safe to write into an XML document.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the escaped string.
|
|
# VAR - String to escape.
|
|
#
|
|
function tEscapeXml() {
|
|
local __RESULT=$1
|
|
local __VAR=${*:2}
|
|
|
|
__VAR=$(echo ${__VAR} | sed -e 's~&~\&~g' -e 's~<~\<~g' -e 's~>~\>~g')
|
|
|
|
eval $__RESULT=\${__VAR}
|
|
}
|
|
|
|
|
|
#
|
|
# Downloads the latest release of a project from GitHub.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the name of the downloaded file.
|
|
# DEVELOPER - GitHub developer name that owns the project.
|
|
# PROJECT - Which project from that developer to download.
|
|
# EXTENSION - Which file extension to download from the release list.
|
|
#
|
|
function tFetchGitHubRelease() {
|
|
local __RESULT=$1
|
|
local __DEVELOPER=$2
|
|
local __PROJECT=$3
|
|
local __EXTENSION=$4
|
|
local __URL=$(curl -s https://api.github.com/repos/${__DEVELOPER}/${__PROJECT}/releases \
|
|
| grep "browser_download_url.*${__EXTENSION}" \
|
|
| cut -d : -f 2,3 \
|
|
| tr -d \" \
|
|
| head -n 1)
|
|
local __FILE=${__URL##*/}
|
|
tDownload ${__URL}
|
|
eval $__RESULT=\${__FILE}
|
|
}
|
|
|
|
|
|
#
|
|
# Displays a whiptail-based file browser for selecting a particular type of file.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to store selected filename, or empty if dialog is canceled.
|
|
# TITLE - Title to display in the file browser window.
|
|
# EXTENSION - The extension to allow to be selected (in ".ext" format).
|
|
# START - Optional. Path to begin browsing from.
|
|
#
|
|
function tFileBrowser() {
|
|
local __RESULT=$1
|
|
local __TITLE=$2
|
|
local __EXTENSION=$3
|
|
local __START=$4
|
|
local __SELECTION=
|
|
local __DIRLIST=()
|
|
local __CURDIR=
|
|
local __RET=
|
|
local __LOOPING=1
|
|
|
|
pushd . > /dev/null
|
|
|
|
if [[ ! -z ${__START} ]]; then
|
|
cd "${__START}"
|
|
fi
|
|
|
|
while [[ ${__LOOPING} -eq 1 ]]; do
|
|
__DIRLIST=()
|
|
if [[ "${__CURDIR}" != "/" ]]; then
|
|
__DIRLIST[${#__DIRLIST[@]}+1]=".."
|
|
__DIRLIST[${#__DIRLIST[@]}+1]="Up"
|
|
fi
|
|
for F in *; do
|
|
if [[ -d "${F}" ]]; then
|
|
__DIRLIST[${#__DIRLIST[@]}+1]=$(echo "${F}/ ")
|
|
else
|
|
__DIRLIST[${#__DIRLIST[@]}+1]=$(echo "${F} ")
|
|
fi
|
|
__DIRLIST[${#__DIRLIST[@]}+1]=$(ls -lah "${F}" | awk '{ print $5 }')
|
|
done
|
|
|
|
__CURDIR=$(pwd)
|
|
|
|
set +e
|
|
__SELECTION=$(whiptail --title "${__TITLE}" \
|
|
--menu "PgUp/PgDn/Arrows ENTER Selects File/Folder or TAB Key\n\n${__CURDIR}" 0 0 0 \
|
|
--cancel-button "Cancel" \
|
|
--ok-button "Select" "${__DIRLIST[@]}" 3>&1 1>&2 2>&3)
|
|
__RET=$?
|
|
set -e
|
|
|
|
if [[ ${__RET} -eq 1 ]]; then
|
|
# User Selected Cancel
|
|
__SELECTION=
|
|
__LOOPING=0
|
|
elif [[ ${__RET} -eq 0 ]]; then
|
|
tTrim __SELECTION "${__SELECTION}"
|
|
if [[ -d "${__SELECTION}" ]]; then
|
|
# Was a directory selected?
|
|
cd "${__SELECTION}"
|
|
elif [[ -f "${__SELECTION}" ]]; then
|
|
# Was a file selected?
|
|
if [[ ${__SELECTION} == *${__EXTENSION} ]]; then
|
|
# Check if file has given extension
|
|
if (whiptail --title "Confirm Selection" \
|
|
--yesno "Directory: ${__CURDIR}\n Filename: ${__SELECTION}" 0 0 \
|
|
--yes-button "Confirm" \
|
|
--no-button "Reselect"); then
|
|
__SELECTION="$(pwd)/${__SELECTION}"
|
|
__LOOPING=0
|
|
fi
|
|
else
|
|
# Not requested extension
|
|
whiptail --title "Error" \
|
|
--msgbox "Not a ${__EXTENSION} file" 0 0
|
|
fi
|
|
else
|
|
# Not a file or folder
|
|
whiptail --title "Error" \
|
|
--msgbox "Unable to change to directory ${__SELECTION}" 0 0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
eval $__RESULT=\${__SELECTION}
|
|
|
|
popd > /dev/null
|
|
}
|
|
|
|
|
|
#
|
|
# Returns the absolute path of a given file.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the absolute path.
|
|
# SOURCE - File which we wish to find the absolute path.
|
|
#
|
|
function tFindPath() {
|
|
local __RESULT=$1
|
|
local __SOURCE=$2
|
|
local __DIR=
|
|
|
|
while [[ -h "${__SOURCE}" ]]; do
|
|
__DIR="$( cd -P "$( dirname "${__SOURCE}" )" && pwd )"
|
|
__SOURCE="$(readlink "${__SOURCE}")"
|
|
# If $__SOURCE was a relative symlink, we need to resolve it
|
|
# relative to the path where the symlink file was located.
|
|
[[ ${__SOURCE} != /* ]] && __SOURCE="${__DIR}/${__SOURCE}"
|
|
done
|
|
__DIR="$( cd -P "$( dirname "${__SOURCE}" )" && pwd )"
|
|
|
|
eval $__RESULT=\${__DIR}
|
|
}
|
|
|
|
|
|
#
|
|
# Returns the location and name of the parent script that included us.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which we want the absolute filename returned.
|
|
#
|
|
function tGetParentPath() {
|
|
local __RESULT=$1
|
|
local __PARENTPATH=
|
|
|
|
# We have to initialize __tG__PARENTPATH before the calling script
|
|
# changes directories elsewhere. We set it on load.
|
|
tFindPath __PARENTPATH "${__tG__PARENTPATH}"
|
|
__TOWELPATH="${__PARENTPATH}/`basename \"${__tG__PARENTPATH}\"`"
|
|
|
|
eval $__RESULT=\${__PARENTPATH}
|
|
}
|
|
|
|
|
|
#
|
|
# Returns the location and name of this Towel script.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which we want the absolute filename returned.
|
|
#
|
|
function tGetTowelPath() {
|
|
local __RESULT=$1
|
|
local __TOWELPATH=
|
|
|
|
tFindPath __TOWELPATH "${BASH_SOURCE[0]}"
|
|
__TOWELPATH="${__TOWELPATH}/`basename \"${BASH_SOURCE[0]}\"`"
|
|
|
|
eval $__RESULT=\${__TOWELPATH}
|
|
}
|
|
|
|
|
|
#
|
|
# Runs program with sudo asking for password using a nice dialog box.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# ARGS - Command to execute with sudo.
|
|
#
|
|
function tSudo() {
|
|
local __ARGS="$@"
|
|
local __ASK="$(mktemp --suffix=.sh -q)"
|
|
|
|
if [[ "${__tG__SUDOPASS}x" == "x" ]]; then
|
|
cat <<- ASKPASS > "${__ASK}"
|
|
#!/bin/bash
|
|
"${BASH_SOURCE[1]}" TOWEL_ASKPASS
|
|
ASKPASS
|
|
else
|
|
cat <<- ASKPASS > "${__ASK}"
|
|
#!/bin/bash
|
|
echo "${__tG__SUDOPASS}"
|
|
ASKPASS
|
|
fi
|
|
|
|
chmod +x "${__ASK}"
|
|
|
|
if [[ "${__ARGS}x" == "x" ]]; then
|
|
SUDO_ASKPASS="${__ASK}" sudo -A -v
|
|
else
|
|
SUDO_ASKPASS="${__ASK}" sudo -A ${__ARGS}
|
|
fi
|
|
|
|
rm "${__ASK}"
|
|
}
|
|
|
|
|
|
#
|
|
# Helper function for asking for password using a nice dialog box.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# None.
|
|
#
|
|
function tSudoAskPass() {
|
|
local __RET=
|
|
local __PASSWORD=
|
|
|
|
set +e
|
|
__PASSWORD=$(
|
|
whiptail --passwordbox "\n$(basename "${BASH_SOURCE[1]}") needs superuser access.\n\nPlease enter your password:" 12 60 --title "Need Root" 3>&1 1>&2 2>&3
|
|
)
|
|
__RET=$?
|
|
set -e
|
|
|
|
if [[ ${__RET} -ne 0 ]]; then
|
|
echo "Cannot continue without superuser access." 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
printf "${__PASSWORD}\n"
|
|
exit 0
|
|
}
|
|
|
|
|
|
#
|
|
# Allows specifying the sudo password for automated installs.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# PASSWORD - The sudo password to use.
|
|
#
|
|
function tSudoSetPassword() {
|
|
local __PASSWORD=$1
|
|
|
|
__tG__SUDOPASS=${__PASSWORD}
|
|
}
|
|
|
|
|
|
#
|
|
# Removes whitespace from the beginning and end of a string.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the trimmed string.
|
|
# VAR - String to trim.
|
|
#
|
|
function tTrim() {
|
|
local __RESULT=$1
|
|
local __VAR="${*:2}"
|
|
|
|
# remove leading whitespace characters
|
|
__VAR="${__VAR#"${__VAR%%[![:space:]]*}"}"
|
|
# remove trailing whitespace characters
|
|
__VAR="${__VAR%"${__VAR##*[![:space:]]}"}"
|
|
|
|
eval $__RESULT=\${__VAR}
|
|
}
|
|
|
|
|
|
#
|
|
# Removes whitespace from the beginning of a string.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the trimmed string.
|
|
# VAR - String to trim.
|
|
#
|
|
function tTrimL() {
|
|
local __RESULT=$1
|
|
local __VAR="${*:2}"
|
|
|
|
# remove leading whitespace characters
|
|
__VAR="${__VAR#"${__VAR%%[![:space:]]*}"}"
|
|
|
|
eval $__RESULT=\${__VAR}
|
|
}
|
|
|
|
|
|
#
|
|
# Removes whitespace from the end of a string.
|
|
#
|
|
# Parameters:
|
|
#
|
|
# RESULT - Variable in which to return the trimmed string.
|
|
# VAR - String to trim.
|
|
#
|
|
function tTrimR() {
|
|
local __RESULT=$1
|
|
local __VAR="${*:2}"
|
|
|
|
# remove trailing whitespace characters
|
|
__VAR="${__VAR%"${__VAR##*[![:space:]]}"}"
|
|
|
|
eval $__RESULT=\${__VAR}
|
|
}
|
|
|
|
|
|
#
|
|
# We're a library. Don't run us.
|
|
#
|
|
if [ -z "${BASH_SOURCE[1]}" ]; then
|
|
echo "This is a library. Include it into other scripts. Don't execute it."
|
|
echo "Visit https://skunkworks.kangaroopunch.com/skunkworks/towel for more."
|
|
exit 0
|
|
fi
|