Files
q-tools/q-tools-functions
2025-03-16 11:53:54 +02:00

596 lines
17 KiB
Plaintext

## improved CD commands:
_QCD_FIND=find
if [[ "$OSTYPE" = "darwin"* ]]; then
alias tac=gtac
_QCD_FIND=gfind
fi
mkdir -p $HOME/.config/qcd/
_QCD_HISTORY=$HOME/.config/qcd/cdhistory
_QCD_BOOKMARKS=$HOME/.config/qcd/bookmarks
if which sqlite3 &>/dev/null; then
_QCD_DB=$HOME/.config/qcd/db
sqlite3 "$_QCD_DB" "CREATE TABLE IF NOT EXISTS history (path TEXT PRIMARY KEY UNIQUE, key TEXT NOT NULL, time INT NOT NULL); CREATE TABLE IF NOT EXISTS bookmarks (key TEXT PRIMARY KEY UNIQUE, path TEXT NOT NULL, time INT NOT NULL);"
fi
if [[ -e "$HOME/.qcd" ]]; then
cat "$HOME/.qcd" >> "$_QCD_BOOKMARKS"
mv "$HOME/.qcd" "$HOME/.qcd.bak"
fi
function gcd() {
# guess cd, find first match in folder, or ask if multiple hits
local cdto QCDPATH
case $OSTYPE in
darwin*) QCDPATH=$( dirname $( realpath ${BASH_SOURCE[0]} ) ) ;;
*) QCDPATH=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) ;;
esac
cdto=$( python3 "$QCDPATH"/files/gcd-findmatch.py "$@" )
[[ -z "$cdto" ]] && return
\cd "$cdto"
} # gcd ends
function hcd() {
if [[ -n "Q_CD_DB" ]]; then
hcd_sqlite "$@"
return $?
fi
# History cd. Run without arguments to see list of entries, number as arg. to change directory.
[[ "$1" = "-h" ]] && {
echo History cd. Run without arguments to see list of entries, number as arg. to change directory.
return
}
local d
if [ -z "$1" ]
then tail -n 20 "$_QCD_HISTORY" | head -n 19 | cut -d: -f2 | tac | cat -n | sed 's,^ \+,,' | tac
return
fi
d=$( tail -n 20 "$_QCD_HISTORY" | head -n 19 | cut -d: -f2 | tac | cat -n | sed 's,^ \+,,' | grep -h "^$1 " )
d=${d/* /}
if [ ! -z "$d" ]
then \cd "$d"
fi
}
function hcd_sqlite() {
# History cd. Run without arguments to see list of entries, number as arg. to change directory.
[[ "$1" = "-h" ]] && {
echo History cd. Run without arguments to see list of entries, number as arg. to change directory.
return
}
local d
if [ -z "$1" ]; then
sqlite3 -column "$_QCD_DB" "SELECT ROW_NUMBER() OVER (ORDER BY time DESC) AS row, path FROM history ORDER BY row LIMIT 20"
return
fi
d=$( sqlite3 -column "$_QCD_DB" "SELECT path FROM (SELECT ROW_NUMBER() OVER (ORDER BY time DESC) AS row, path FROM history) WHERE row = $1" )
if [ ! -z "$d" ]
then \cd "$d"
fi
}
function cd_history () {
# Function that replaces "cd". It stores visited folder in ~/.bash_cdhistory
local old
local b
if [ -z "$1" ]
then \cd "$HOME"
return
fi
\cd "$1"
b=$( basename "$PWD" )
b=${b:0:42}
if [[ -n "$_QCD_DB" ]]; then
local cd_time
printf -v cd_time "%(%s)T000" -1
sqlite3 "$_QCD_DB" "INSERT OR REPLACE INTO history (path,key,time) VALUES ('$PWD','$b',$cd_time);" &>/dev/null || true
else
touch "$_QCD_HISTORY"
old=$( tail -n 499 "$_QCD_HISTORY" )
echo "$old" > "$_QCD_HISTORY"
b=$( basename "$PWD" )
echo "$b:$PWD" >> "$_QCD_HISTORY"
fi
}
alias cd=cd_history
function qcd_sqlite() {
# cd command, that jumps to folders visited in the near history, or user bookmarked folders
local OPTIND
local OPTARG
local opt
local case
local d
while getopts ae:hiILl:m opt
do case "$opt" in
a)
# Adding
local name=${!OPTIND}
# Remove / chars in name
name=${name//\//}
name=${name//:/}
if [ -z "$name" ]
then name=$( basename $( pwd ))
fi
echo "$name":$PWD
local cd_time
printf -v cd_time "%(%s)T000" -1
sqlite3 "$_QCD_DB" "INSERT OR REPLACE INTO bookmarks (path,key,time) VALUES ('$PWD','$name',$cd_time);" &>/dev/null || true
return
;;
i)
case="-i"
;;
I)
sqlite3 "$_QCD_DB" "CREATE TABLE IF NOT EXISTS history (path TEXT PRIMARY KEY UNIQUE, key TEXT NOT NULL, time INT NOT NULL); CREATE TABLE IF NOT EXISTS bookmarks (key TEXT PRIMARY KEY UNIQUE, path TEXT NOT NULL, time INT NOT NULL);"
return
;;
L)
echo "## History ##"
sqlite3 -column "$_QCD_DB" "SELECT key,path FROM (SELECT key, path, time FROM history ORDER BY time DESC LIMIT 500) ORDER BY time"
echo
echo "## Bookmarks ##"
sqlite3 -column "$_QCD_DB" "SELECT key, path FROM bookmarks ORDER BY key"
return
;;
l)
local d=$( sqlite3 "$_QCD_DB" "SELECT path FROM bookmarks WHERE key LIKE '$OPTARG%' LIMIT 1" )
if [[ -z "$d" ]]; then
d=$( sqlite3 "$_QCD_DB" "SELECT path FROM (SELECT key, path, time FROM history ORDER BY time DESC) WHERE key LIKE '$OPTARG%' LIMIT 1" )
fi
echo $d
return
;;
e)
local d=$( sqlite3 "$_QCD_DB" "SELECT path FROM bookmarks WHERE key LIKE '$OPTARG%' LIMIT 1" )
if [[ -z "$d" ]]; then
d=$( sqlite3 "$_QCD_DB" "SELECT path FROM (SELECT key, path, time FROM history ORDER BY time DESC) WHERE key LIKE '$OPTARG%' LIMIT 1" )
fi
echo QCD=$d
QCD="$d"
return
;;
m)
sqlite3 -column "$_QCD_DB" "DROP TABLE IF EXISTS tmp_history"
sqlite3 -column "$_QCD_DB" "CREATE TABLE tmp_history AS
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY time DESC) AS row,key,path,time FROM history ORDER BY row) WHERE row < 10000"
sqlite3 -column "$_QCD_DB" "DELETE FROM history"
sqlite3 -column "$_QCD_DB" "INSERT INTO history SELECT path,key,time FROM tmp_history"
sqlite3 -column "$_QCD_DB" "DROP TABLE IF EXISTS tmp_history"
sqlite3 -column "$_QCD_DB" "CREATE TABLE IF NOT EXISTS tmp_notexists (path TEXT); DELETE FROM tmp_notexists;"
local db_dir
while read db_dir; do
if [[ ! -d "$db_dir" ]]; then
sqlite3 "$_QCD_DB" "INSERT INTO tmp_notexists (path) VALUES ('$db_dir');" &>/dev/null || true
fi
done < <( sqlite3 "$_QCD_DB" "SELECT path FROM history" )
sqlite3 -column "$_QCD_DB" "DELETE FROM history WHERE path IN ( SELECT path FROM tmp_notexists );"
return
;;
h)
echo 'qcd [-hiLm]|[-al] [name]
Version: 2024-10-14
Change current working path based on the sqlite3 db '"$_QCD_DB"'
-a [name] Adds the path to the list
You may add the name of the path, but when omitted
the basename will be used
-e [name] Show the match, store in variable QCD
-i Case insensitive search, must come first.
-I Install (use only once)
-l [name] Show the match, but do not change
-L Lists the paths
-m Maintain the list, deleting non-existing entries'
return
;;
esac
done
shift $(($OPTIND - 1))
if [ -z "$1" ]
then [[ $OPTIND -gt 1 ]] && return
\cd; return
fi
unset OPTSTRING
unset OPTIND
if [ "$1" = "-" ]
then hcd_sqlite 1
return
fi
local d=$( sqlite3 "$_QCD_DB" "SELECT path FROM bookmarks WHERE key LIKE '$1%' LIMIT 1" )
if [[ -z "$d" ]]; then
d=$( sqlite3 "$_QCD_DB" "SELECT path FROM (SELECT key, path, time FROM history ORDER BY time DESC) WHERE key LIKE '$1%' LIMIT 1" )
fi
if [ ! -z "$d" ]
then \cd "$d"
fi
}
function qcd() {
if [[ -n "$_QCD_DB" ]]; then
qcd_sqlite "$@"
return $?
fi
# cd command, that jumps to folders visited in the near history, or user bookmarked folders
local OPTIND
local OPTARG
local opt
local case
local d
[ -e "$_QCD_BOOKMARKS" ] || touch "$_QCD_BOOKMARKS"
[ -e "$_QCD_HISTORY" ] || touch "$_QCD_HISTORY"
while getopts ae:hiILl:m opt
do case "$opt" in
a)
# Adding
local name=${!OPTIND}
# Remove / chars in name
name=${name//\//}
name=${name//:/}
if [ -z "$name" ]
then name=$( basename $( pwd ))
fi
echo "$name":$( pwd )
echo "$name":$( pwd ) >> "$_QCD_BOOKMARKS"
return
;;
i)
case="-i"
;;
I)
touch "$_QCD_BOOKMARKS" "$_QCD_HISTORY"
return
;;
L)
echo "## History $_QCD_HISTORY ##"
cat "$_QCD_HISTORY"
echo
echo "## Saved $_QCD_BOOKMARKS ##"
cat "$_QCD_BOOKMARKS"
return
;;
l)
local d=$( grep $case -h ^"$OPTARG" "$_QCD_BOOKMARKS" <( tac "$_QCD_HISTORY" ) | head -n 1 )
d=${d/*:/}
echo $d
return
;;
e)
local d=$( grep $case -h ^"$OPTARG" "$_QCD_BOOKMARKS" <( tac "$_QCD_HISTORY" ) | head -n 1 )
d="${d/*:/}"
echo QCD=$d
QCD="$d"
return
;;
m)
local IFS=$'\n'
touch "$_QCD_BOOKMARKS" "$_QCD_HISTORY"
touch "$_QCD_BOOKMARKS".tmp "$_QCD_HISTORY".tmp
for line in $( cat "$_QCD_HISTORY" );
do if [ -d "${line/*:/}" ];
then echo "$line" >> "$_QCD_HISTORY".tmp
fi
done
mv "$_QCD_HISTORY".tmp "$_QCD_HISTORY"
for line in $( cat "$_QCD_BOOKMARKS" );
do if [ -d "${line/*:/}" ];
then echo "$line" >> "$_QCD_BOOKMARKS".tmp
fi
done
mv "$_QCD_BOOKMARKS".tmp "$_QCD_BOOKMARKS"
return
;;
h)
echo 'qcd [-hiLm]|[-al] [name]
Version: 2022-10-14
Change current working path based on the list '"$_QCD_BOOKMARKS"'
Keeps a history of folders visited in '"$_QCD_HISTORY"'
-a [name] Adds the path to the list
You may add the name of the path, but when omitted
the basename will be used
-e [name] Show the match, store in variable QCD
-i Case insensitive search, must come first.
-I Install (use only once)
-l [name] Show the match, but do not change
-L Lists the paths
-m Maintain the list, deleting non-existing entries'
return
;;
esac
done
shift $(($OPTIND - 1))
if [ -z "$1" ]
then [[ $OPTIND -gt 1 ]] && return
\cd; return
fi
unset OPTSTRING
unset OPTIND
if [ "$1" = "-" ]
then d=$( tail -n 1 "$_QCD_HISTORY" )
d=${d/*:/}
\cd "$d"
return
fi
d=$( grep $case -h ^"$1" "$_QCD_BOOKMARKS" <( tac "$_QCD_HISTORY" ) | head -n 1 )
d=${d/*:/}
if [ ! -z "$d" ]
then \cd "$d"
fi
}
_qcd()
{
local cur
local d
cur=${COMP_WORDS[COMP_CWORD]}
if [[ "$cur" == -* ]]; then
COMPREPLY=( $( compgen -W "-a -i -h -l -L -m" -- $cur ) )
return 0
fi
COMPREPLY=( $( compgen -W "$( grep -h ^"$cur" "$_QCD_BOOKMARKS" <( tac "$_QCD_HISTORY" ) | cut -d: -f1 )" ) )
}
complete -F _qcd qcd
# qcd ends
function ncd() {
# echo Next sibling cd
[[ "$1" = "-h" ]] && {
echo -e "Next sibling cd: change directory to [../next_folder]. \nncd - to go to previous folder. \nncd . to descend to first subfolder."
return
}
local _current_pwd _iter_d _iter_prev _current_found
_current_pwd=$( basename "$( pwd )" )
if [ "$1" = "." ]; then
for _iter_d in *; do
if [ ! -d "$_iter_d" ]; then continue; fi
\cd "$_iter_d"
return
done
return
fi
\cd ..
for _iter_d in *; do
if [ ! -d "$_iter_d" ]; then continue; fi
if [ -n "$_current_found" ]; then
\cd "$_iter_d"
return
fi
if [ "$_iter_d" = "$_current_pwd" ]; then
_current_found=true
if [ "$1" = "-" ]; then
if [ -z "$_iter_prev" ]; then
echo $_current_pwd was the first folder.
else
\cd "$_iter_prev";
return
fi
fi
fi
_iter_prev="$_iter_d"
done
if [ -n "$_current_found" ]; then
echo "$_current_pwd" was the last folder.
else
echo "$_current_pwd" was not found in ../
fi
}
function scd() {
# search cd, find first recursively match in a folder
[[ "$1" = "-h" ]] && {
echo Search cd. First argument path to search, optionally second argument where to search from.
return
}
local cdto cdfrom
[[ -z "$2" ]] && {
cdfrom="."
} || {
cdfrom="$2"
}
cdto=$( $_QCD_FIND "$cdfrom" -mindepth 1 -type d -iname "*$1*" | sort -V | head -n 1 )
echo $cdto
[[ -z "$cdto" ]] && return
\cd "$cdto"
} # scd ends
function wcd() {
# cd to folder that matches "which" dirname
if [[ "$1" = "-h" ]] || [[ -z "$1" ]]; then
echo which-cd. Give executable name to cd to the path of the executable
return
fi
\cd $( dirname $( which "$1" ) )
}
## File processing functions
function foldermenu_prompt {
# Function to add in PS prompt variable, enabling foldermenu support
[ -f .foldermenu ] && {
foldermenu -lf 10 $@ || echo -n "*"
} || {
true
}
}
function igrep () {
# Interactive grep, read grep arguments
[[ "$1" = "-h" ]] && [[ "$#" -eq 1 ]] && {
echo Interactive grep, read grep arguments
return
}
local args
args="-i -e "
while true
do read -e -i "$args" args
echo $@
grep --color=auto $args "$@"
if [ "$?" -ne 0 ]
then echo _no_matches_ | grep --color=auto ".*"
fi
done
}
function ised () {
# Interactive sed
[[ "$1" = "-h" ]] && [[ "$#" -eq 1 ]] && {
echo Interactive sed, read sed arguments
return
}
local args
if [ ! -f "$1" ]; then echo must give atleast one file; return 1;fi;
args="-e s/string//g"
while true
do read -e -i "$args" args
echo input files: "$@"
eval sed $args "$@"
if [ "$?" -ne 0 ]
then echo Error output | grep --color=auto ".*"
fi
done
}
function rmv () {
# mv files/folders with rsync
[[ "$1" = "-h" ]] && {
echo 'mv files/folders with rsync (shows speed and progress)'
echo 'Usage: rmv source [source2] [source3] target'
return
}
local sources
sources=()
# remove / from ends, if target is an existing folder
for (( i=1; i<=$(($#-1)); i++ ))
do if [ -d "${@: -1}" ]
then sources+=("${!i%/}")
else sources+=("${!i}")
fi
done
rsync -vaP --remove-source-files "${sources[@]}" "${@: -1}"
# remove empty folders from sources (not last argument)
for (( i=1; i<=$(($#-1)); i++ ))
do if [ -d "${!i}" ]
then $_QCD_FIND "${!i}" -depth -type d -exec rmdir \{\} \;
fi
done
}
function whenfilechanges() {
# Run a command when file(s) time stamp changes
echo "Version: 2015-10-13"
[ -z "$2" ] && { echo 'Usage: whenfilechanges "file" "some" "command"
Follows the modification time of the "file" and runs the command
at every change.
The "file" may also be a glob e.g. "*tex" '
return
}
local fname
local forced
local otime
local ntime
local i
fname=($1)
echo Waiting for ${#fname[@]} files to change: ${fname[@]}
shift 1
echo "Command: \"$@\""
otime=()
for ((i=0;i<${#fname[@]};i++))
do [ -e "${fname[$i]}" ] || {
echo "File: ${fname[$i]} not found!"
return
}
otime[$i]=$( stat -c %Z "${fname[$i]}" )
done
while :; do
ntime=()
for ((i=0;i<${#fname[@]};i++)); do
ntime[$i]=$( stat -c %Z "${fname[$i]}" )
[[ "${ntime[$i]}" -ne "${otime[$i]}" ]] && {
echo "${fname[$i]} changed:"
otime[$i]=$( stat -c %Z "${fname[$i]}" )
eval "$@"
echo -n "Waiting for file changes... "
date
}
done
echo -ne Waiting.. $( date )\\r
read -t 2 forced
[ $? -eq 0 ] && eval "$@"
done
}
# Shell enhancement
function qbg {
# Run a command quiet, and backgrounded
[[ "$1" = "-h" ]] && {
echo 'Run a command quiet, and backgrounded ( typically X11 executable )'
return
}
"$@" &> /dev/null &
}
_qbg()
{
local cur
cur=${COMP_WORDS[COMP_CWORD]}
if [[ "$COMP_CWORD" == 1 ]]; then
local executables
executables=$({ compgen -c; compgen -abkA function; } | sort | uniq -u )
COMPREPLY=( $( compgen -W "$executables" -- "$cur" ) )
return 0
fi
COMPREPLY=( $( compgen -fd -- "$cur" ) )
}
complete -F _qbg qbg
function set_term_title {
# set term in byobu/screen xterm etc..
[ -z "$1" ] && {
echo -ne '\033k'$HOSTNAME'\033\\\r'
} || {
echo -ne '\033k'$1'\033\\\r'
}
}
function path_add_current {
# Add current or given folder to PATH
[[ "$1" = "-h" ]] && {
echo 'Add the current folder in PATH, or pass a folder name to be added'
return
}
local p=$(pwd)
[[ -z "$1" ]] || {
case $OSTYPE in
darwin*) p=$( realpath "$1" ) ;;
*) p=$( realpath "$1" ) ;;
esac
}
[[ -z "$p" ]] && {
echo Path "$1" not found >&2
return
}
export PATH="${p}:$PATH"
path_remove_duplicates
echo PATH=$PATH
}
function path_remove_duplicates {
# Remove duplicates in PATH
PATH=$( echo $PATH | awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' | sed 's,:\+$,,g' )
export PATH
}
function whichcat {
which "$1" | xargs cat
}