#!/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
}
new( ) {
if [ $# -eq 2 ] ; then
title " ${ 2 #modules/ } "
else
title " $1 "
fi
command new.pl " $@ "
title " $LAST_TITLE "
}
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
}
dum( ) {
du -max " ${ @ :- . } " |
sort -k1n |
tail -n 10000
}
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
# don't local PASSWORD or default case will not export PASSWORD, changing case to work around
local 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
#passvar="${1:-PASSWORD}"
for passvar in " ${ @ :- PASSWORD } " ; do
export " $passvar " = " $password "
done
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
}