#!/usr/bin/env python3 import sys, os, re import difflib as dl # Define color codes Green = "\033[32;1m" Red = "\033[31;1m" Reset = "\033[0m" # Available shortcut keys key_list = "1234567890qwertyuiop" class getch: def get(self): import termios, tty 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() def errexit(s): printerr(s) sys.exit(1) def colorize_matches(directories, repl): return [repl.sub(r"%s\g<1>%s" % (Green, Reset), d) for d in directories] def search_close_match(directories, key): """ Return a list of Components whose name closely matches key """ match = [] key = key.lower() for name in directories: s = dl.SequenceMatcher(None, name.lower(), key) if s.ratio() > 0: match.append((s.ratio(), name)) match.sort(key=lambda x: x[0], reverse=True) best_matches = [x[1] for x in match[0:10]] return best_matches def get_dirs(pat_dir): # list sub-folders in the folder current_folders = [ d for d in os.listdir(pat_dir) if os.path.isdir(os.path.join(pat_dir, d)) ] current_folders = [d for d in current_folders if not d.startswith(".")] current_folders.sort() return current_folders def main(): # Print help if len(sys.argv) == 1 or sys.argv[-1] == "-h": errexit( "Guess cd: Find first match in folder, interactive if multiple matches\n Arguments: [dir/]pattern_to_match_folders" ) 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): errexit("%s: %sNo such folder%s" % (pat_dir, Red, Reset)) # list sub-folders in the folder current_folders = get_dirs(pat_dir) # no sub-folders if len(current_folders) == 0: errexit("There are no subfolders in %s" % (pat_dir,)) # 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 = colorize_matches(matching_folders, repl_case) # 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 = colorize_matches(matching_folders, repl_nocase) # No matches, try close match if len(matching_folders) == 0: matching_folders = search_close_match(current_folders, pat_base) matches = [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) # 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...") key_in = getch().get() try: key_index = key_list.index(key_in) key_match = matching_folders[key_index] except (ValueError, IndexError) as err: errexit("%s'%s' Not in the list%s" % (Red, key_in, Reset)) print(os.path.join(pat_dir, key_match)) if __name__ == "__main__": main()