#!/usr/bin/python3 """Fuzzy-find a file and check which package owns it. Dependencies ============ * fzf * mlocate """ import argparse import subprocess # ========== Constants ========== # Commands PACMAN_CMD = "/usr/bin/pacman" FZF_CMD = "/usr/bin/fzf" FZF_OPTS = ["--read0", "--select-1", "--exit-0", "--print0"] LOCATE_CMD = "/usr/bin/locate" LOCATE_OPTS = ["--all", "--ignore-case", "--null"] LOCALE = "utf-8" # ========== Functions ========== def run_fzf(files): """Run fzf on a stream of searched files for the user to select. :param files: stream of null-terminated files to read :type files: bytes stream (stdout of a completed process) :returns: selected file :rtype: str """ selected_file = subprocess.run( [FZF_CMD] + FZF_OPTS, input=files, stdout=subprocess.PIPE ).stdout return selected_file.decode(LOCALE).strip("\x00") def locate_files(patterns): """Use a locate-based program to locate files, then pass to fzf. :param patterns: patterns to pass to locate :type patterns: list :returns: path of user-selected file :rtype: bytes """ cmd = [LOCATE_CMD] + LOCATE_OPTS cmd.extend(patterns) return subprocess.run(cmd, capture_output=True).stdout # ========== Main Script ========== def main(): parser = argparse.ArgumentParser() parser.add_argument("patterns", nargs="+", help="file pattern to search for") args = parser.parse_args() try: files = locate_files(args.patterns) # Locate binary is optional for package, but required for script except FileNotFoundError: print("locate binary not found, install and re-run script") else: selected_file = run_fzf(files) subprocess.run([PACMAN_CMD, "-Qo", selected_file]) if __name__ == "__main__": main()