## improved CD commands: function gcd() { # guess cd, find first match in folder, or ask if multiple hits local cdto QCDPATH QCDPATH=$( dirname ${BASH_SOURCE[0]} ) cdto=$( python "$QCDPATH"/files/gcd-findmatch.py "$@" ) [[ -z "$cdto" ]] && return \cd "$cdto" } # gcd ends function hcd() { # 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 "$HOME/.bash_cdhistory" | head -n 19 | cut -d: -f2 | tac | cat -n | sed 's,^ \+,,' | tac return fi d=$( tail -n 20 "$HOME/.bash_cdhistory" | head -n 19 | cut -d: -f2 | tac | cat -n | sed 's,^ \+,,' | grep -h "^$1 " ) d=${d/* /} if [ ! -z "$d" ] then \cd "$d" fi } function cd_history () { # Function that replaces "cd". It stores visited folder in ~/.bash_cdhistory local old local p local b if [ -z "$1" ] then \cd "$HOME" return fi touch "$HOME/.bash_cdhistory" old=$( tail -n 149 "$HOME/.bash_cdhistory" ) echo "$old" > "$HOME/.bash_cdhistory" \cd "$1" p=$( pwd ) b=$( basename "$p" ) echo $b":"$p >> "$HOME/.bash_cdhistory" } alias cd=cd_history function qcd() { # 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 ] || touch ~/.qcd [ -e ~/.bash_cdhistory ] || touch ~/.bash_cdhistory while getopts ae:hiILl:m opt do case "$opt" in a) # Adding local name=${!OPTIND} if [ -z "$name" ] then name=$( basename $( pwd )) fi echo "$name":$( pwd ) echo "$name":$( pwd ) >> ~/.qcd return ;; i) case="-i" ;; I) echo "See ~/.bash_completion for possible duplicates" echo ' have qcd && _qcd() { local cur local d COMPREPLY=() 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 <( tac ~/.bash_cdhistory ) | cut -d: -f1 )" ) ) } complete -F _qcd qcd ' >> ~/.bash_completion . /etc/bash_completion touch ~/.qcd ~/.bash_cdhistory return ;; L) echo ==History ~/.bash_cdhistory: cat ~/.bash_cdhistory echo echo ==Saved ~/.qcd: cat ~/.qcd return ;; l) local d=$( grep $case -h ^"$OPTARG" ~/.qcd <( tac ~/.bash_cdhistory ) | head -n 1 ) d=${d/*:/} echo $d return ;; e) local d=$( grep $case -h ^"$OPTARG" ~/.qcd <( tac ~/.bash_cdhistory ) | head -n 1 ) d="${d/*:/}" echo QCD=$d QCD="$d" return ;; m) local IFS=$'\n' touch ~/.qcd ~/.bash_cdhistory touch ~/.qcd.tmp ~/.bash_cdhistory.tmp for line in $( cat ~/.bash_cdhistory ); do if [ -d "${line/*:/}" ]; then echo "$line" >> ~/.bash_cdhistory.tmp fi done mv ~/.bash_cdhistory.tmp ~/.bash_cdhistory for line in $( cat ~/.qcd ); do if [ -d "${line/*:/}" ]; then echo "$line" >> ~/.qcd.tmp fi done mv ~/.qcd.tmp ~/.qcd return ;; h) echo 'qcd [-hiLm]|[-al] [name] Version: 2017-03-24 Change current working path based on the list ~/.qcd Keeps a history of folders visited in ~/.bash_cdhistory -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, with autocompletion (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 ~/.bash_cdhistory ) d=${d/*:/} \cd "$d" return fi d=$( grep $case -h ^"$1" ~/.qcd <( tac ~/.bash_cdhistory ) | head -n 1 ) d=${d/*:/} if [ ! -z "$d" ] then \cd "$d" fi } # qcd ends function ncd() { # echo Next sibling cd [[ "$1" = "-h" ]] && { echo Next sibling cd: change directory to [../next_folder]. ncd - to go to previous. return } local _current_pwd _iter_d _iter_prev _current_found _current_pwd=$( basename $( pwd ) ) \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=$( find "$cdfrom" -mindepth 1 -type d -iname "*$1*" | sort -V | head -n 1 ) echo $cdto [[ -z "$cdto" ]] && return \cd "$cdto" } # scd ends ## 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 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 & } 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" ]] || { p=$( readlink -e "$1" ) } [[ -z "$p" ]] && { echo Path "$1" not found >&2 return } export PATH=$( echo ${p}:$PATH | awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' | sed 's,:\+$,,g' ) echo PATH=$PATH }