You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
613 lines
15 KiB
Bash
613 lines
15 KiB
Bash
#!/usr/bin/env bash
|
|
# shellcheck disable=SC2230
|
|
# vim:ts=4:sts=4:sw=4:et
|
|
#
|
|
# Author: Hari Sekhon
|
|
# Date: circa 2006 (forked from .bashrc)
|
|
#
|
|
# https://github.com/harisekhon/bash-tools
|
|
#
|
|
# License: see accompanying Hari Sekhon LICENSE file
|
|
#
|
|
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
|
|
#
|
|
# https://www.linkedin.com/in/harisekhon
|
|
#
|
|
|
|
# ============================================================================ #
|
|
# B a s h G e n e r a l F u n c t i o n s
|
|
# ============================================================================ #
|
|
|
|
bash_tools="${bash_tools:-$(dirname "${BASH_SOURCE[0]}")/..}"
|
|
|
|
# shellcheck disable=SC1090
|
|
. "$bash_tools/.bash.d/os_detection.sh"
|
|
|
|
# Enables colourized return codes in prompt_func
|
|
# better leave it as the same as already set. This way a reload of bashrc doesn't change the mode
|
|
# could do retmode=${retmode:-off} but this is unnecessary overhead
|
|
#retmode=off
|
|
# safer to set it to off because otherwise it's possible to get in to a loop with set -u
|
|
retmode=${retmode:-off}
|
|
retmode(){
|
|
if [ "$retmode" = "on" ]; then
|
|
retmode=off
|
|
echo "retmode off"
|
|
else
|
|
retmode=on
|
|
echo "retmode on"
|
|
fi
|
|
}
|
|
|
|
pg(){
|
|
# don't want pgrep, want color coding
|
|
# shellcheck disable=SC2009
|
|
ps -ef |
|
|
grep -i --color=yes "$@" |
|
|
grep -v grep
|
|
}
|
|
|
|
checkprog(){
|
|
if type -P "$1" &>/dev/null; then
|
|
return 0
|
|
else
|
|
echo "$1 could not be found in path"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
typer(){
|
|
local alias_target
|
|
local type_output
|
|
for x in "$@"; do
|
|
type_output="$(type "$x")"
|
|
# shellcheck disable=SC2119
|
|
alias_target="$(
|
|
awk '/aliased to/{print $5}' <<< "$type_output" |
|
|
unquote
|
|
)"
|
|
if [ -n "$alias_target" ]; then
|
|
echo "$type_output"
|
|
typer "$alias_target"
|
|
else
|
|
type "$x"
|
|
fi
|
|
done
|
|
}
|
|
|
|
lld(){
|
|
{
|
|
local target="$1"
|
|
ls -ld "$target"
|
|
[ "$target" = "/" ] && return
|
|
lld "$(dirname "$target")"
|
|
} | column -t
|
|
}
|
|
|
|
# shellcheck disable=SC2120
|
|
unquote(){
|
|
sed '
|
|
s/^[[:space:]]*[`'"'"'"]//;
|
|
s/[`'"'"'"][[:space:]]*$//;
|
|
' "$@"
|
|
}
|
|
|
|
bell_done(){
|
|
eval "$@"
|
|
print '\a'
|
|
}
|
|
|
|
resolve_symlinks(){
|
|
local readlink=readlink
|
|
if isMac; then
|
|
if type -P greadlink &>/dev/null; then
|
|
readlink=greadlink
|
|
else
|
|
readlink=""
|
|
fi
|
|
fi
|
|
if [ -z "$readlink" ]; then
|
|
echo "$*"
|
|
return
|
|
fi
|
|
for x in "$@"; do
|
|
"$readlink" -m "$x"
|
|
done
|
|
}
|
|
|
|
# for all files listed, return the highest directory - useful for pushd to the right git root following symlinks before doing git diff and commmits, used by gitu() in git.sh which is called in inline vimrc 'nmap ;;'
|
|
basedir(){
|
|
local dir_list=""
|
|
for x in "$@"; do
|
|
dir_list="$dir_list $(dirname "$x")"
|
|
done
|
|
# assumes they share the same base and that the shortest one will be right - could put more comparison here and return error if not
|
|
local output
|
|
output="$(tr ' ' '\n' <<< "$dir_list" | grep -v '^[[:space:]]*$' | sort | head -n 1)"
|
|
if [ -z "$output" ]; then
|
|
echo "ERROR: empty basedir"
|
|
return 1
|
|
fi
|
|
echo "$output"
|
|
}
|
|
|
|
toLower(){
|
|
tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
toUpper(){
|
|
tr '[:lower:]' '[:upper:]'
|
|
}
|
|
|
|
trim(){
|
|
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' "$@"
|
|
}
|
|
|
|
normalize_spaces(){
|
|
# not variant of + \+ works on Mac
|
|
#sed 's/[[:space:]]\+/ /'
|
|
# flattens out newlines, which changes behaviour in scripts like check_git_commit_authors.sh
|
|
#perl -pe 's/\s+/ /g'
|
|
# horizontal newlines, don't match \n
|
|
perl -pe 's/\h+/ /g'
|
|
}
|
|
|
|
remove_last_column(){
|
|
awk '{$NF=""; print $0}'
|
|
}
|
|
|
|
strip_basedirs(){
|
|
local basedir="$1"
|
|
shift
|
|
for x in "$@"; do
|
|
y="${x#${basedir%%/}/}"
|
|
y="${y##/}"
|
|
echo "$y"
|
|
done
|
|
}
|
|
|
|
user(){
|
|
read -r -p 'user: ' USERNAME
|
|
export USERNAME
|
|
if [ -z "${PASSWORD:-}" ]; then
|
|
pass
|
|
fi
|
|
}
|
|
|
|
pass(){
|
|
# doesn't echo, we can do better by making it star for each char
|
|
#read -r -s -p 'password: ' PASSWORD
|
|
PASSWORD=""
|
|
prompt="Enter password: "
|
|
while IFS= read -p "$prompt" -r -s -n 1 char; do
|
|
if [[ "$char" == $'\0' ]]; then
|
|
break
|
|
fi
|
|
prompt='*'
|
|
PASSWORD="${PASSWORD}${char}"
|
|
done
|
|
export PASSWORD
|
|
echo
|
|
}
|
|
|
|
unpass(){
|
|
unset PASSWORD
|
|
}
|
|
|
|
hr(){
|
|
echo "# ============================================================================ #"
|
|
}
|
|
|
|
repeat(){
|
|
local i n
|
|
n="$1"
|
|
shift
|
|
if [ -z "$n" ]; then
|
|
echo "usage: repeat N command args"
|
|
return 1
|
|
fi
|
|
for ((i=1; i <= n; i++)); do
|
|
"$@"
|
|
done
|
|
}
|
|
|
|
loop(){
|
|
while true; do
|
|
eval "${*//\$/\\$}"
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
ptop(){
|
|
if [ -z "$1" ]; then
|
|
echo "usage: ptop program1 program2 ..."
|
|
return 1
|
|
fi
|
|
local pids
|
|
#pids="$(pgrep -f "$(sed 's/ /|/g' <<< "$*")")"
|
|
pids="$(pgrep -f "${*// /|}")"
|
|
local pid_args
|
|
if isMac; then
|
|
# shellcheck disable=SC2001
|
|
pid_args="$(sed 's/^/-pid /' <<< "$pids")"
|
|
else
|
|
# shellcheck disable=SC2001
|
|
pid_args="$(sed 's/^/-p /' <<< "$pids")"
|
|
fi
|
|
if [ -z "$pids" ]; then
|
|
echo "No matching programs found"
|
|
return 1
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
top $pid_args
|
|
}
|
|
|
|
topcommands(){
|
|
# first awk print $2 but my advanced history records `date '+%F %T'` in between number and command for $2 and $3, making command $4
|
|
history |
|
|
awk '{print $4}' |
|
|
awk 'BEGIN {FS="|"} {print $1}' |
|
|
sort |
|
|
uniq -c |
|
|
sort -n |
|
|
tail -n "${1:-10}" |
|
|
sort -nr
|
|
}
|
|
alias topcmds=topcommands
|
|
|
|
# easy quick find recursing down current directory tree
|
|
#
|
|
#f(){
|
|
# [ -n "$*" ] || { echo "usage: f <partial_pattern>"; return 1; }
|
|
# pattern=""
|
|
# for x in $*; do
|
|
# pattern+="*$x"
|
|
# done
|
|
# pattern+="*"
|
|
# find -L . -iname "$pattern"
|
|
#}
|
|
#
|
|
# shellcheck disable=SC2032
|
|
f(){
|
|
local grep=""
|
|
# shellcheck disable=SC2013
|
|
#for x in $(sed 's/[^A-Za-z0-9]/ /g' <<< "$*"); do
|
|
for x in "${@//[^A-Za-z]/_}"; do
|
|
if [[ "$x" =~ [a-zA-Z0-9] ]]; then
|
|
grep="$grep | grep -i --color=auto $x"
|
|
fi
|
|
done
|
|
# times about the same
|
|
#eval find -L . -type f -iname "\*$1\*" $grep
|
|
eval find -L . -type f "$grep"
|
|
}
|
|
|
|
dgrep(){
|
|
local pattern="$*"
|
|
# auto-exported in aliases.sh when iterating git repos
|
|
# shellcheck disable=SC2154
|
|
ls "$docs/"*"${pattern// /}"* 2>/dev/null
|
|
# shellcheck disable=SC2046,SC2033
|
|
grep -iER "$pattern" $(find ~/docs "$docs" -type f -maxdepth 1 2>/dev/null | grep -v '/\.')
|
|
}
|
|
|
|
diffl(){
|
|
diff "$@" | less
|
|
}
|
|
|
|
foreachfile(){
|
|
# not passing function f()
|
|
# shellcheck disable=SC2033
|
|
find . -type f -maxdepth 1 |
|
|
while read -r file; do
|
|
[ ! -f "$file" ] && continue
|
|
[ -b "$file" ] && continue
|
|
[ -c "$file" ] && continue
|
|
[ -d "$file" ] && continue
|
|
[ -p "$file" ] && continue
|
|
[ -S "$file" ] && continue
|
|
[ -L "$file" ] && continue
|
|
eval "$@"
|
|
done
|
|
}
|
|
|
|
# vim which
|
|
# vw() moved to vim.sh
|
|
|
|
# file which
|
|
fw(){
|
|
local path
|
|
for x in "$@"; do
|
|
path="$(which "$x")"
|
|
if [ -z "$path" ]; then
|
|
return 1
|
|
fi
|
|
file "$path"
|
|
echo
|
|
# shellcheck disable=SC2086
|
|
ls -l $LS_OPTIONS "$path"
|
|
done
|
|
}
|
|
|
|
cdwhich(){
|
|
local path
|
|
local directory
|
|
if [ $# -ne 1 ]; then
|
|
echo "usage: cdwhich programname"
|
|
return 1
|
|
fi
|
|
path="$(which "$1")"
|
|
if [ -z "$path" ]; then
|
|
echo
|
|
echo "$1 could not be found in \$PATH"
|
|
return 1
|
|
fi
|
|
directory="$(dirname "$path")"
|
|
if [ -z "$directory" ]; then
|
|
echo "cannot find directory for $path"
|
|
return 2
|
|
fi
|
|
echo "$directory"
|
|
cd "$directory" || return 1
|
|
}
|
|
|
|
add_etc_host(){
|
|
local host_line="$*"
|
|
# $sudo is set in .bashrc if needed
|
|
# shellcheck disable=SC2154
|
|
$sudo grep -q "^$host_line" /etc/hosts ||
|
|
$sudo echo "$host_line" >> /etc/hosts
|
|
}
|
|
|
|
# vihosts() moved to vim.sh
|
|
|
|
proxy(){
|
|
export proxy_host="${1:-${proxy_host:-localhost}}"
|
|
export proxy_port="${2:-${proxy_port:-8080}}"
|
|
export proxy_port_ssl="${3:-${proxy_port_ssl:-8443}}"
|
|
export proxy_user="${4:-${proxy_user:-$USER}}"
|
|
if [ -z "$proxy_password" ]; then
|
|
read -r -s -p 'proxy password: ' proxy_password
|
|
fi
|
|
export http_proxy="http://$proxy_user:$proxy_password@$proxy_host:$proxy_port"
|
|
export https_proxy="https://$proxy_user:$proxy_password@$proxy_host:$proxy_port_ssl"
|
|
# MiniShift respects these next three
|
|
export HTTP_PROXY="$http_proxy"
|
|
export HTTPS_PROXY="$https_proxy"
|
|
export NO_PROXY=".local,.localdomain,.intra,169.254.169.254" # works only on suffixes or IP addresses - ignore the EC2 Metadata API address
|
|
export ftp_proxy="$http_proxy" # might need to replace protocol prefix here, would check, but who even uses ftp any more
|
|
JAVA_NO_PROXY="$(sed 's/^/*/;s/,/|*/g' <<< "$NO_PROXY")"
|
|
# strip the additions we just added off the end so that we don't end up with dups if running proxy more than once
|
|
JAVA_OPTS="${JAVA_OPTS%%-Dhttp.proxyHost*}"
|
|
export JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyHost=$proxy_host -Dhttp.proxyPort=$proxy_port -Dhttp.proxyUser=$proxy_user -Dhttp.proxyPassword=$proxy_password -Dhttps.proxyHost=$proxy_host -Dhttps.proxyPort=$proxy_port_ssl -DnonProxyHosts='$JAVA_NO_PROXY'"
|
|
export SBT_OPTS="$JAVA_OPTS"
|
|
}
|
|
|
|
paste_clipboard(){
|
|
if isMac; then
|
|
cat | pbcopy
|
|
elif isLinux; then
|
|
cat | xclip
|
|
else
|
|
echo "ERROR: OS is not Darwin/Linux"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
readlink(){
|
|
if isMac; then
|
|
greadlink "$@"
|
|
else
|
|
command readlink "$@"
|
|
fi
|
|
}
|
|
|
|
# see also readlink (beware differs between Linux and Mac)
|
|
# this works on imaginary paths
|
|
abspath(){
|
|
if [ -z "$1" ]; then
|
|
echo "NO PATH GIVEN!"
|
|
return 1
|
|
fi
|
|
# shellcheck disable=SC2001
|
|
sed 's@^\./@'"$PWD"'/@;
|
|
s@^\([^\./]\)@'"$PWD"'/\1@;
|
|
s@^\.\./@'"${PWD%/*}"'/@;
|
|
s@/../@/@g;
|
|
s@/\./@/@g;
|
|
s@\(.*\/?\)\.\./?$@\1/@;
|
|
s@//@/@g;
|
|
s@/$@@;' <<< "$1"
|
|
}
|
|
|
|
wcbash(){
|
|
# $github defined in aliases.sh
|
|
# shellcheck disable=SC2154
|
|
wc ~/.bashrc \
|
|
~/.bash_profile \
|
|
~/.bash_logout \
|
|
~/.alias* \
|
|
~/.aliases* \
|
|
~/.bashrc_dynamichosts \
|
|
"$github/bash-tools/.bashrc" \
|
|
"$github/bash-tools/.bash_profile" \
|
|
"$github/bash-tools/.bash.d/"*.sh 2>/dev/null
|
|
}
|
|
|
|
epoch2date(){
|
|
if isMac; then
|
|
date -r "$1"
|
|
else
|
|
date -d "@$1"
|
|
fi
|
|
}
|
|
|
|
pdf(){
|
|
if ! [[ "$1" =~ .*.pdf$ ]]; then
|
|
echo "'$1' does not end in .pdf!"
|
|
return 1
|
|
fi
|
|
if ! [ -f "$1" ]; then
|
|
echo "file not found: $1"
|
|
return 1
|
|
fi
|
|
if isMac; then
|
|
open "$1"
|
|
return $?
|
|
fi
|
|
for x in acroread evince xpdf; do
|
|
if type -P "$x" &>/dev/null; then
|
|
echo "opening with $x..."
|
|
"$x" "$1" &
|
|
return $?
|
|
fi
|
|
done
|
|
echo "Error cannot find acroread, evince or xpdf in PATH."
|
|
return 1
|
|
}
|
|
|
|
currentScreenResolution(){
|
|
#xrandr | awk '/\*/ {print $1}'
|
|
xdpyinfo | awk '/dimensions/ {print $2}'
|
|
}
|
|
|
|
yy(){
|
|
cal "$(date '+%Y')"
|
|
}
|
|
|
|
# ============================================================================ #
|
|
|
|
timestamp(){
|
|
printf "%s" "$(date '+%F %T') $*"
|
|
[ $# -gt 0 ] && printf '\n'
|
|
}
|
|
alias tstamp=timestamp
|
|
|
|
timestampcmd(){
|
|
local output
|
|
output="$(eval "$@" 2>&1)"
|
|
timestamp "$output"
|
|
}
|
|
alias tstampcmd=timestampcmd
|
|
|
|
# ============================================================================ #
|
|
|
|
bak(){
|
|
# TODO: switch this to a .backupstore folder for keeping this stuff instead
|
|
for filename in "$@"; do
|
|
[ -n "$filename" ] || { echo "usage: bak filename"; return 1; }
|
|
[ -f "$filename" ] || { echo "file '$filename' does not exist"; return 1; }
|
|
[[ $filename =~ .*\.bak\..* ]] && continue
|
|
local bakfile
|
|
bakfile="$filename.bak.$(date '+%F_%T' | sed 's/:/-/g')"
|
|
until ! [ -f "$bakfile" ]; do
|
|
echo "WARNING: bakfile '$bakfile' already exists, retrying with a new timestamp"
|
|
sleep 1
|
|
bakfile="$filename.bak.$(date '+%F_%T' | sed 's/:/-/g')"
|
|
done
|
|
cp -av "$filename" "$bakfile"
|
|
done
|
|
}
|
|
|
|
unbak(){
|
|
# restores the most recent backup of a file
|
|
for filename in "$@"; do
|
|
#[ -n "$filename" -o "${filename: -4}" != ".bak" ] || { echo "usage: unbak filename.bak"; return 1; }
|
|
[ -n "$filename" ] || { echo "usage: unbak filename"; return 1; }
|
|
#[ -f "$filename" ] || { echo "file '$filename' does not exist"; return 1; }
|
|
local bakfile
|
|
# don't use -t switch, we want the newest by name, not one that got touched recently
|
|
bakfile="$(find . -path "./$filename.bak.*" 2>/dev/null | sort | tail -n 1)"
|
|
echo "restoring $bakfile"
|
|
cp -av "$bakfile" "$filename"
|
|
done
|
|
}
|
|
|
|
orig(){
|
|
if [ $# -lt 1 ]; then
|
|
echo "usage: orig file1 file2 file3 ..."
|
|
return 1
|
|
fi
|
|
for filename in "$@"; do
|
|
[ -f "$filename" ] || { echo "file '$filename' does not exist"; return 1; }
|
|
[ -f "$filename.org" ] && { echo "$filename.orig already exists, aborting..."; return 1; }
|
|
done
|
|
for filename in "$@"; do
|
|
cp -av "$filename" "$filename.orig"
|
|
done
|
|
}
|
|
|
|
unorig(){
|
|
if [ $# -lt 1 ]; then
|
|
echo "usage: unorig file1.orig file2.orig file3.orig ..."
|
|
return 1
|
|
fi
|
|
for filename in "$@"; do
|
|
if [ -z "$filename" ] || [ "${filename: -5}" != ".orig" ]; then
|
|
echo "usage: unorig filename.orig"
|
|
return 1
|
|
fi
|
|
if ! [ -f "$filename" ]; then
|
|
echo "file '$filename' does not exist"
|
|
return 1
|
|
fi
|
|
done
|
|
for filename in "$@"; do
|
|
cp -av "$filename" "${filename%.orig}"
|
|
done
|
|
}
|
|
|
|
# ============================================================================ #
|
|
|
|
strLastIndexOf(){
|
|
local str="$1"
|
|
local substr="$2"
|
|
local remainder="${str##*$substr}"
|
|
local lastIndex=$((${#str} - ${#remainder}))
|
|
echo $lastIndex
|
|
}
|
|
|
|
# ============================================================================ #
|
|
|
|
progs(){
|
|
# not passing function f()
|
|
# shellcheck disable=SC2033
|
|
find "${@:-.}" -type f |
|
|
grep -Evf ~/code_regex_exclude.txt |
|
|
grep -v -e '/lib/' -e '.*-env.sh' -e '/tests/'
|
|
}
|
|
|
|
progs2(){
|
|
# not passing function f()
|
|
# shellcheck disable=SC2033
|
|
find "${@:-.}" -type f -o -type l |
|
|
grep -Evf ~/code_regex_exclude.txt
|
|
}
|
|
|
|
findpy(){
|
|
# not passing function f()
|
|
# shellcheck disable=SC2033
|
|
find "${@:-.}" -type f -iname '*.py' -o -iname '*.jy' |
|
|
grep -vf ~/code_regex_exclude.txt
|
|
}
|
|
|
|
# ============================================================================ #
|
|
|
|
ldapmaxuid(){
|
|
ldapsearch -x -W "uidNumber=*" uidNumber |
|
|
sed 's/#.*//' |
|
|
grep -v "^[[:space:]]*$" |
|
|
grep uidNumber |
|
|
sort -k2n |
|
|
tail -n1
|
|
}
|
|
|
|
ldapmaxuidgid(){
|
|
ldapsearch -xW -x -W "(|(objectClass=posixAccount)(objectClass=posixGroup))" uidNumber gidNumber |
|
|
sed 's/#.*//' |
|
|
grep --color=auto -v "^[[:space:]]*$" |
|
|
grep -R --color=auto "(uidNumber|gidNumber)" |
|
|
sort -k2n |
|
|
tail -n1
|
|
}
|