diff --git a/files/gcd-findmatch.py b/files/gcd-findmatch.py new file mode 100755 index 0000000..0dcd77c --- /dev/null +++ b/files/gcd-findmatch.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import sys,os,re + +Green="\033[32;1m" +Red="\033[31;1m" +Reset="\033[0m" +key_list="1234567890qwertyuiop" +class getch: + def get(self): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch +def printerr(s): + sys.stderr.write(s+"\n") + sys.stderr.flush() +# Print help +if len(sys.argv)==1 or sys.argv[-1]=="-h": + printerr("Guess cd: Find first match in folder, interactive if multiple matches\n Arguments: [dir/]pattern_to_match_folders") +if sys.argv[-1]=="-h": + sys.exit(0) + +pattern=".".join(sys.argv[1:]) +pat_base=os.path.basename(pattern) +pat_dir=os.path.dirname(pattern) + +if pat_dir=="": + pat_dir="." + +if not os.path.exists(pat_dir): + printerr("%s: %sNo such folder%s"%(pat_dir,Red,Reset)) + sys.exit(1) +# list sub-folders in the folder +current_folders=[d for d in os.listdir(pat_dir) if os.path.isdir(d)] +current_folders=[d for d in current_folders if not d.startswith(".")] +current_folders.sort() + +# find matches +match_case=re.compile(".*"+pat_base+".*") +repl_case=re.compile("("+pat_base+")") + +matching_folders=[d for d in current_folders if match_case.match(d)] +matches=[repl_case.sub("%s\g<1>%s"%(Green,Reset),d) for d in matching_folders] + +# no matches, try case insensitive +if len(matching_folders)==0: + match_nocase=re.compile(".*"+pat_base+".*",re.I) + repl_nocase=re.compile("("+pat_base+")",re.I) + matching_folders=[d for d in current_folders if match_nocase.match(d)] + matches=[repl_nocase.sub("%s\g<1>%s"%(Green,Reset),d) for d in matching_folders] + +# One match, print and return +if len(matching_folders)==1: + printerr(matches[0]) + print(os.path.join(pat_dir,matching_folders[0])) + sys.exit(0) +# No matches +if len(matching_folders)==0: + printerr("%sno matches%s"%(Red,Reset)) + sys.exit(1) +# Many matches, ask the user for folder index +for i,m in enumerate(matches[0:len(key_list)]): + printerr(key_list[i]+": "+m) +if len(matches)>len(key_list): + printerr("Skipping the rest...") +import termios,tty +ch=getch() +key_in=ch.get() +try: + key_index=key_list.index(key_in) + key_match=matching_folders[key_index] +except (ValueError,IndexError) as err: + printerr("%s'%s' Not in the list%s"%(Red,key_in,Reset)) + sys.exit(1) +print(os.path.join(pat_dir,key_match)) diff --git a/qcd_function b/qcd_function index 721cc99..90a8ffc 100644 --- a/qcd_function +++ b/qcd_function @@ -1,3 +1,33 @@ + +## 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() { + [[ "$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 @@ -17,21 +47,6 @@ function cd_history () { } alias cd=cd_history -function hcd() { - # History cd. Run without arguments to see list of entries, give number to change cwd. - 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 qcd() { # cd command, that jumps to folders visited in the near history, or user bookmarked folders local OPTIND @@ -120,7 +135,7 @@ complete -F _qcd qcd ;; h) echo 'qcd [-hiLm]|[-al] [name] -Version: 2015-10-13 +Version: 2017-03-24 Change current working path based on the list ~/.qcd Keeps a history of folders visited in ~/.bash_cdhistory @@ -156,6 +171,112 @@ Keeps a history of folders visited in ~/.bash_cdhistory then \cd "$d" fi +} # qcd ends + +function ncd() { + [[ "$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 +} + +## 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 () { + [[ "$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 () { + [[ "$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 () { + [[ "$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() { @@ -205,184 +326,22 @@ function whenfilechanges() { } -function gcd() { - # guess cd, find first match in folder, or ask if multiple hits - local cdto - { cdto=$( python -c ' -import sys,os,re -if len(sys.argv)==1 or sys.argv[-1]=="-h": - print("Arguments: [dir/]pattern_to_match_folders") -if sys.argv[-1]=="-h": - sys.exit(0) -def G(): - return "\033[32;1m" -def R(): - return "\033[31;1m" -def Z(): - return "\033[0m" -def CS(): - return "1234567890qwertyuiop" -class getch: - def get(self): - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - -pattern=".".join(sys.argv[1:]) - -pat_dir=os.path.dirname(pattern) -if pat_dir=="": - pat_dir="." -pat_base=os.path.basename(pattern) -if not os.path.exists(pat_dir): - print(pat_dir+": "+R()+"No such folder"+Z()) - sys.exit(1) -current_folders=[d for d in os.listdir(pat_dir) if os.path.isdir(d)] -current_folders=[d for d in current_folders if not d.startswith(".")] -current_folders.sort() - -match_case=re.compile(".*"+pat_base+".*") -repl_case=re.compile("("+pat_base+")") - -matching_folders=[d for d in current_folders if match_case.match(d)] -matches=[repl_case.sub(G()+"\g<1>"+Z(),d) for d in matching_folders] - -if len(matching_folders)==0: - match_nocase=re.compile(".*"+pat_base+".*",re.I) - repl_nocase=re.compile("("+pat_base+")",re.I) - matching_folders=[d for d in current_folders if match_nocase.match(d)] - matches=[repl_nocase.sub(G()+"\g<1>"+Z(),d) for d in matching_folders] - -if len(matching_folders)==1: - print(matches[0]) - sys.stderr.write(os.path.join(pat_dir,matching_folders[0])) - sys.exit(0) -if len(matching_folders)==0: - print(R()+"no matches"+Z()) - sys.exit(1) -for i,m in enumerate(matches[0:20]): - print(CS()[i]+": "+m) -if len(matches)>20: - print("Skipping rest...") -import termios,tty -ch=getch() -key_in=ch.get() -try: - key_index=CS().index(key_in) - key_match=matching_folders[key_index] -except ValueError,IndexError: - sys.exit(1) -sys.stderr.write(os.path.join(pat_dir,key_match)) -' "$@" 2>&1 1>&$out); } {out}>&1 ; \cd "$cdto" -} - -function ncd() { - # Next cd: cd to ../[next_folder]. ncd - to go to previous - 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 rmv () { - # mv files/folders with rsync (shows speed and progress) - 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 igrep () { - # Interactive grep - 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 - 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 foldermenu_prompt { - # Function to add in PS prompt variable, enabling foldermenu support - [ -f .foldermenu ] && { - foldermenu -lf 10 $@ || echo -n "*" - } || { - true - } -} +# Shell enhancement function qbg { - # Run a program 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 -e '\033k'$HOSTNAME'\033\\' + echo -ne '\033k'$HOSTNAME'\033\\\r' } || { - echo -e '\033k'$1'\033\\' + echo -ne '\033k'$1'\033\\\r' } }