Bash-Funk “misc” module
The following statements are automatically executed when this module loads:
function -timeout() {
if [[ $# < 2 || ${1:-} == "--help" ]]; then
echo "Usage: ${FUNCNAME[0]} TIMEOUT COMMAND [ARG]..."
echo "Executes the COMMAND and aborts if it does not finish within the given TIMEOUT in seconds."
[[ ${1:-} == "--help" ]] && return 0 || return 1
fi
if hash timeout 2>/dev/null; then
timeout "$@"
elif hash gtimeout 2>/dev/null; then
# MacOS: https://stackoverflow.com/a/21118126/5116073
gtimeout "$@"
else
# see: http://mywiki.wooledge.org/BashFAQ/068
perl -e 'alarm shift; exec @ARGV' "$@"
fi
}
The following commands are available when this module is loaded:
- -choose
- -env
- -help
- -please
- -reload
- -root
- -test-all-misc
- -tweak-bash
- -update
- -var-exists
- -wait
- -weather
License
SPDX-FileCopyrightText: © Vegard IT GmbH (https://vegardit.com)
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-choose
Usage: -choose [OPTION]... OPTION1 [OPTION]...
Prompts the user to choose one entry of the given list of options.
Parameters:
OPTION (1 or more required)
Allowed options to choose from.
Options:
--assign VARNAME
Assigns the selected value to the variable with the given name instead of printing to stdout.
--default OPTION
The option to pre-selected.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
local selectedIndex=0 ESC=$(echo -e "\033") redraw=1 index dialogFD option
# when in a subshell use stderr to render the dialog, so capturing stdout will only contain the selected value
[ -t 1 ] && dialogFD=1 || dialogFD=2
# pre-select if default value was given
if [[ ${_default:-} ]]; then
for index in "${!_OPTION[@]}"; do
if [[ $_default == ${_OPTION[$index]} ]]; then
selectedIndex=$index
fi
done
fi
while true; do
if [[ $redraw ]]; then
for index in "${!_OPTION[@]}"; do
option="${_OPTION[$index]}"
option="${option//$'\n'/\\n}"
option="${option:0:$(( COLUMNS - 6 ))}"
if (( index == selectedIndex )); then
>&$dialogFD echo -e " \033[1m* $option\033[22m"
else
>&$dialogFD echo " $option"
fi
done
fi
local key= key2= key3= key4=
read -sN1 key && \
read -sN1 -t 0.001 key2 && \
read -sN1 -t 0.001 key3 && \
read -sN1 -t 0.001 key4 || true
key=${key}${key2}${key3}${key4}
case $key in
${ESC}*A)
if (( selectedIndex > 0 )); then
(( selectedIndex-- ))
redraw=1
fi
;;
${ESC}*B)
if (( selectedIndex + 1 < ${#_OPTION[@]} )); then
(( selectedIndex++ ))
redraw=1
fi
;;
$ESC)
echo >&2
echo "Aborting on user request" >&2
return 1
;;
*)
if [[ $key == "" || $key == $'\n' ]]; then
if [[ $_assign ]]; then
eval "$_assign=\"${_OPTION[$selectedIndex]//\"/\\\"}\""
else
echo "${_OPTION[$selectedIndex]}";
fi
return 0
fi
redraw=
;;
esac
if [[ $redraw ]]; then
-cursor-pos --fd $dialogFD --up "$(( ${#_OPTION[@]} ))"
fi
done
-env
Usage: -env [OPTION]...
Prints all exported environment variables.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
export -p | cut -d' ' -f3-
-help
Usage: -help [OPTION]...
Prints the online help of all bash-funk commands.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
for helpfunc in $(compgen -A function -- -help-); do
$helpfunc
done | sort
-please
Usage: -please [OPTION]...
Re-runs the previously entered command with sudo.
Requirements:
+ Command 'sudo' must be available.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
local cmd="$(echo $(fc -ln -1))"
if [[ $cmd == sudo* ]]; then
echo "-please: Last command '$cmd' was already executed with sudo."
return 1
elif [[ $cmd == -please* ]]; then
echo "-please: Executing last command '$cmd' with sudo has no use."
return 1
fi
[[ $__interactive ]] && echo -e "Executing last command [\033[35m$cmd\033[0m] with sudo..." || true
sudo "$BASH" -c "$cmd"
-reload
Usage: -reload [OPTION]...
Reloads bash-funk.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
if [[ ! ${BASH_FUNK_ROOT} ]]; then
echo "-reload: Error: BASH_FUNK_ROOT variable is not defined."
return 1
fi
if [[ ! -r ${BASH_FUNK_ROOT}/bash-funk.sh ]]; then
echo "-reload: Error: File [${BASH_FUNK_ROOT}/bash-funk.sh] is not readable by user [$USER]."
return 1
fi
source ${BASH_FUNK_ROOT}/bash-funk.sh
-root
Usage: -root [OPTION]...
Starts an interactive shell as root user. Same as 'sudo -i'.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
sudo -i
-test-all-misc
Usage: -test-all-misc [OPTION]...
Performs a selftest of all functions of this module by executing each function with option '--selftest'.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
-choose --selftest && echo || return 1
-env --selftest && echo || return 1
-help --selftest && echo || return 1
-please --selftest && echo || return 1
-reload --selftest && echo || return 1
-root --selftest && echo || return 1
-tweak-bash --selftest && echo || return 1
-update --selftest && echo || return 1
-var-exists --selftest && echo || return 1
-wait --selftest && echo || return 1
-weather --selftest && echo || return 1
-tweak-bash
Usage: -tweak-bash [OPTION]...
Performs some usability configurations of Bash.
Options:
-v, --verbose
Prints additional information during command execution.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
#
# enable and configure command history
#
set -o history
export HISTFILE=~/.bash_funk_history
export HISTSIZE=10000
export HISTFILESIZE=$HISTSIZE
export HISTCONTROL=ignorespace:ignoredups
export HISTTIMEFORMAT="%F %T "
export HISTIGNORE="&:?:??:clear:exit:pwd"
history -r
#
# Readline productivity tweaks, see https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File-Syntax.html
#
if [[ $- == *i* ]]; then
bind '"\e[A": history-search-backward' # enable history searching backward using arrow-up
bind '"\e[B": history-search-forward' # enable history searching forward using arrow-down
bind 'set enable-keypad on' # try to enable the application keypad
# improve auto-completion
bind '"\e[6~": menu-complete' # enable Pg-Up/Down to cycle through completion candidates
bind '"\e[5~": menu-complete-backward' # enable Pg-Up/Down to cycle through completion candidates
bind 'set show-all-if-ambiguous on' # show words which have more than one possible completion immediately instead of ringing the bell
bind 'set show-all-if-unmodified on' # show words which have more than one possible completion without any possible partial completion immediately instead of ringing the bell.
bind 'set colored-completion-prefix on' 2>/dev/null # highlights the common prefix of the set of possible completions. Requires Bash 4.4 or higher
bind 'set completion-ignore-case on' # perform case-insensitive filename matching and completion
fi
# make ls colorful by default except on MacOS where it is not supported
[[ $OSTYPE == "darwin"* ]] || alias -- ls="command ls --color=auto"
#
# aliases
#
alias -- grep="command grep --colour=auto"
alias -- gh='command history | grep'
alias -- l="ll"
alias -- ll="-ll"
alias -- ++="-cd-down"
alias -- --="-cd-hist"
alias -- ..="-cd-up"
alias -- ...="command cd ../.."
alias -- -="command cd -"
if hash mc 2>/dev/null && [[ -e /usr/lib/mc/mc-wrapper.sh ]]; then
# see https://stackoverflow.com/questions/39017391/how-to-make-midnight-commander-exit-to-its-current-directory
alias mc='. /usr/lib/mc/mc-wrapper.sh'
fi
#
# Bash productivity options, see http://wiki.bash-hackers.org/internals/shell_options
#
local opt opts=(autocd checkwinsize dirspell direxpand extglob globstar histappend)
for opt in ${opts[@]}; do
if shopt -s $opt &>/dev/null; then
[[ $_verbose ]] && echo "shopt -s $opt => ENABLED"
else
[[ $_verbose ]] && echo "shopt -s $opt => UNSUPPORTED"
fi
done
#
# cygwin/msys tweaks
#
case "$OSTYPE" in
cygwin)
for drive in {a..z}; do
if [[ -e /cygdrive/${drive} ]]; then
alias -- "${drive}:"="cd /cygdrive/${drive}"
alias -- "${drive^^}:"="cd /cygdrive/${drive}"
fi
done
if ! hash sudo &>/dev/null; then
alias -- sudo="cygstart --action=runas"
fi
;;
msys)
for drive in {a..z}; do
if [[ -e /${drive} ]]; then
alias -- "${drive}:"="cd /${drive}"
alias -- "${drive^^}:"="cd /${drive}"
fi
done
if ! hash sudo &>/dev/null; then
alias -- sudo="cygstart --action=runas"
fi
;;
esac
-update
Usage: -update [OPTION]...
Updates bash-funk to the latest code from github (https://github.com/vegardit/bash-funk). All local modifications are overwritten.
Options:
-r, --reload
Reloads the bash-funk after updating.
-y, --yes
Answer interactive prompts with 'yes'.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
if [[ ! ${BASH_FUNK_ROOT} ]]; then
echo "-update: Error: BASH_FUNK_ROOT variable is not defined."
return 1
fi
if [[ ! -w ${BASH_FUNK_ROOT} ]]; then
echo "-update: Error: Directory [${BASH_FUNK_ROOT}] is not writeable by user [$USER]."
return 1
fi
if [[ ! $_yes ]]; then
read -p "Are you sure you want to update bash-funk located in [${BASH_FUNK_ROOT}]? (y) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "-update: Aborting on user request."
return 0
fi
fi
# update via SVN
if [[ -e "${BASH_FUNK_ROOT}/.svn" ]]; then
svn revert -R "${BASH_FUNK_ROOT}" || return
svn update "${BASH_FUNK_ROOT}" || return
[[ $_reload ]] && -reload || true
return
fi
# update via Git
if [[ -e "${BASH_FUNK_ROOT}/.git" ]]; then
( cd "${BASH_FUNK_ROOT}" && git config core.autocrlf false && git fetch && git reset origin/main --hard && git pull ) || return
[[ $_reload ]] && -reload || true
return
fi
# update via curl/wget
local get
if hash curl &>/dev/null; then
get="curl -#L"
else
if wget --help | grep -- --show-progress &>/dev/null; then
get="wget -qO- --show-progress"
else
get="wget -qO-"
fi
fi
( cd "${BASH_FUNK_ROOT}" && $get https://github.com/vegardit/bash-funk/tarball/main | tar -xzv --strip-components 1 ) || return
[[ $_reload ]] && -reload || true
return
-var-exists
Usage: -var-exists [OPTION]... VARIABLE_NAME
Determines if the given variable is declared.
Parameters:
VARIABLE_NAME (required)
Name of the Bash variable to check.
Options:
-v, --verbose
Prints additional information during command execution.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Examples:
$ -var-exists USER
$ -var-exists -v USER
Bash variable 'USER' exists.
$ -var-exists -v NON_EXISTANT_VARIABLE
Bash variable 'NON_EXISTANT_VARIABLE' does not exist.
Implementation:
if ${!_VARIABLE_NAME+false}; then
[[ $_verbose ]] && echo "Bash variable '$_VARIABLE_NAME' does not exist." || true
return 1
else
[[ $_verbose ]] && echo "Bash variable '$_VARIABLE_NAME' exists." || true
return 0
fi
-wait
Usage: -wait [OPTION]... SECONDS
Waits for the given number of seconds or until the key 's' pressed.
Parameters:
SECONDS (required)
Number of seconds to wait.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
local green="\033[1;32m"
local reset="\033[0m"
local saveCursor="\033[s"
local restoreCursor="\033[u"
local cursor9Right="\033[9C"
local cursor9Left="\033[9D"
echo -ne "Waiting for [$(date +%T --date=@$(($_SECONDS - 3600)))] until $(date +%T --date=@$(($(date +%s) + $_SECONDS))). Press [s] to skip: $cursor9Right"
for (( i = 0; i < _SECONDS; i++ )); do
if [[ $__interactive ]]; then
local newLine=
else
# adding a \n new line character to the end of the line to make the output parseable by sed which is line oriented
local newLine="$saveCursor\n$restoreCursor"
fi
echo -ne "$cursor9Left$green$(date +%T --date=@$(($_SECONDS - ${i} - 3600))) $reset$newLine"
local char=
read -s -n1 -t1 char || :
[[ $char == "s" ]] && break
done
echo
-weather
Usage: -weather [OPTION]... [LOCATION]
Displays local weather conditions/forecast. Uses https://wttr.in.
Parameters:
LOCATION
The location (name or GPS coordinates) for the weather region. If not specified location is determined based on public IP address.
Options:
--color [WHEN] (default: 'auto', one of: [always,auto,never])
Indicates when to colorize the output.
-f, --forcast [DAYS] (default: '1', integer: 0-3)
Number of days to show weather forecast (1=today, 2=today+tomorrow,...).
-g, --geoservice [ID] (default: 'ubuntu.com', one of: [ubuntu.com,ip-api.com,ipapi.co,ipinfo.io])
The geo location serivce to be used when auto-detecting the location.
-l, --lang [LANG] (default: 'en')
Language, e.g. 'en', 'de'.
-u, --units UNITS (one of: [m,u])
System of measurement units to be used: m = metric, u = USCS.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Examples:
$ -weather Berlin
\ / Sunny
.-. 26 °C
― ( ) ― ↗ 6 km/h
\`-’ 11 km
/ \ 0.0 mm
$ -weather Paris -l fr
.-. Pluie légère
( ). +4(-2) °C
(___(__) ↗ 33 km/h
‘ ‘ ‘ ‘ 10 km
‘ ‘ ‘ ‘ 0.2 mm
Implementation:
local request_options+="${_units:-}${_forcast:-2}"
case "${_color:-auto}" in
never) request_options+='T' ;;
auto) if ! -ansi-colors-supported 8 || [[ ! -t 1 ]]; then request_options+='T'; fi ;;
esac
hash wget &>/dev/null && local http_get="wget --timeout 5 -qO-" || local http_get="curl -sSf --max-time 5"
if [[ -z $_LOCATION ]]; then
case ${_geoservice:-ipinfo.io} in
ubuntu.com) _LOCATION=$($http_get http://geoip.ubuntu.com/lookup | sed 's/^.*<Latitude>\([0-9.]\{,7\}\)<\/Latitude><Longitude>\([0-9.]\{,7\}\).*$/\1,\2/') ;;
ip-api.com) _LOCATION=$($http_get ip-api.com/line/?fields=lat,lon | paste -sd ',' -) ;;
ipapi.co) _LOCATION=$($http_get https://ipapi.co/latlong) ;;
ipinfo.io) _LOCATION=$($http_get https://ipinfo.io | grep loc | cut -d'"' -f4) ;;
esac
fi
$http_get --header "Accept-Language: ${_lang:-en}" wttr.in/$_LOCATION?$request_options